Python Argparse - How can I add text to the default help message? - python

I'm using python's argparse to handle parsing of arguments.
I get a default help message structured like so:
usage: ProgramName [-h] ...
Description
positional arguments:
...
optional arguments:
-h, --help show this help message and exit
...
What I want is to add an entire new section to this message, for example:
usage: ProgramName [-h] ...
Description
positional arguments:
...
optional arguments:
-h, --help show this help message and exit
...
additional information:
This will show additional information relevant to the user.
....
Is there a way to achieve this behavior?
A solution that is supported by both python 2.7 and 3.x is preferred.
Edit:
I would also rather have a solution that will add the new section / sections at the bottom of the help message.

You can quite do it using epilog.
Here is an example below:
import argparse
import textwrap
parser = argparse.ArgumentParser(
prog='ProgramName',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=textwrap.dedent('''\
additional information:
I have indented it
exactly the way
I want it
'''))
parser.add_argument('--foo', nargs='?', help='foo help')
parser.add_argument('bar', nargs='+', help='bar help')
parser.print_help()
Result :
usage: ProgramName [-h] [--foo [FOO]] bar [bar ...]
positional arguments:
bar bar help
optional arguments:
-h, --help show this help message and exit
--foo [FOO] foo help
additional information:
I have indented it
exactly the way
I want it

There are multiple ways in which you can add a description to your command.
The recommended way is to add a module documentation at the top of your source code file as in:
""" This is the description, it will be accessible within the variable
__doc__
"""
And then:
parser = argparse.ArgumentParser(description=__doc__)
To add text below the parameter description, use epilog, as shown in the following example taken from the documentation:
>>> parser = argparse.ArgumentParser(description='A foo that bars',
epilog="And that's how you'd foo a bar")
>>> parser.print_help()
usage: argparse.py [-h]
A foo that bars
optional arguments: -h, --help show this help message and exit
And that's how you'd foo a bar
Refer to the documentation (linked above) for more information.

Related

argparse sub-commands and groups: Setting help-dialog in sub-command on its own group without being hidden in top-level help-dialog

I'm trying to achieve a program that makes both use of sub-commands (e.g.: program sub-command [options]) and groups (which makes for a fancy help dialog).
I have achieved this goal with one minor exception: In order to get the help dialog in its own group I must add the flag add_help=False when creating the sub-command parser, which removes the help message when running the top-level help dialog (e.g.: program -h).
Here's the code I've developed:
# imports
import argparse
# create the top-level parser
parser = argparse.ArgumentParser(prog="example", add_help=False, epilog="A very cool program")
# add top-level groups
toplevel = parser.add_argument_group("Global arguments")
toplevel.add_argument("-g", "--global", action="store_true", help="A global argument.")
help = parser.add_argument_group("Help dialog")
help.add_argument("-h", "--help", action="help", default=argparse.SUPPRESS, help="Show this help message and exit.")
# create subparser
subparsers = parser.add_subparsers(title="Available subcommands", dest="subcommand")
# create the parser for the "a" subcommand
parser_a = subparsers.add_parser("a", add_help=False)
# add groups for subcommand "a"
required_a = parser_a.add_argument_group("Required arguments")
required_a.add_argument("--bar", type=int, help="Flag bar help", required=True)
help_a = parser_a.add_argument_group("Help dialog")
help_a.add_argument("-h", "--help", action="help", default=argparse.SUPPRESS, help="Show this help message and exit.")
# create the parser for the "b" command
parser_b = subparsers.add_parser("b", add_help=False)
# add groups for subcommand "b"
required_b = parser_b.add_argument_group("Required arguments")
required_b.add_argument("--baz", help="Flag baz help", required=True)
optional_b = parser_b.add_argument_group("Optional arguments")
optional_b.add_argument("--tas", help="Flag tas help")
help_b = parser_b.add_argument_group("Help dialog")
help_b.add_argument("-h", "--help", action="help", default=argparse.SUPPRESS, help="Show this help message and exit.")
# parse arguments
args = parser.parse_args()
# provide args to main
print(args)
The top-level help is as follows:
$ example -h
usage: example [-g] [-h] {a,b} ...
Global arguments:
-g, --global A global argument.
Help dialog:
-h, --help Show this help message and exit.
Available sub-commands:
{a,b}
A very cool program
Which as you can see doesn't show the help message on the sub-commands.
To have them show up I would have to get rid of add_help=False when creating parser_a and parser_b but then, as expected, it would raise an issue when I would define my own help flag.
Essentially I would like to have the best of both worlds, where my main dialog would be:
$ example -h
usage: example [-g] [-h] {a,b} ...
Global arguments:
-g, --global A global argument.
Help dialog:
-h, --help Show this help message and exit.
Available sub-commands:
{a,b}
a Help for sub-command a.
b Help for sub-command b.
A very cool program
And the sub-commands would be:
$ example a -h
usage: example a --bar BAR [-h]
Required arguments:
--bar BAR Flag bar help
Help dialog:
-h, --help Show this help message and exit.
After going through the argparse source code, would the option conflict_handler be a possible solution? Would it be possible to tell it to ignore the original help dialog, which shows under positional arguments which I do not want, and have it instead showing in my own group but without disabling it completely?
TL;DR: Looking for modifications required to my Python script such that argparse outputs the two previous code blocks.
Note: The reason why "help-dialog" is written in the title is because stack overflow does not allow me to write the world "help" on the title, regardless of where it is written in the sentence.
Your code produces:
1138:~/mypy$ python3 stack69633930.py -h
usage: example [-g] [-h] {a,b} ...
Global arguments:
-g, --global A global argument.
Help dialog:
-h, --help Show this help message and exit.
Available subcommands:
{a,b}
A very cool program
1140:~/mypy$ python3 stack69633930.py a -h
usage: example a --bar BAR [-h]
Required arguments:
--bar BAR Flag bar help
Help dialog:
-h, --help Show this help message and exit.
With the conventional help
1140:~/mypy$ python3 stack69633930-1.py -h
usage: example [-h] [-g] {a,b} ...
optional arguments:
-h, --help show this help message and exit
Global arguments:
-g, --global A global argument.
Available subcommands:
{a,b}
A very cool program
1142:~/mypy$ python3 stack69633930-1.py a -h
usage: example a [-h] --bar BAR
optional arguments:
-h, --help show this help message and exit
Required arguments:
--bar BAR Flag bar help
The only differences I see are "Help dialog" group instead of "optional arguments", and the place of the [-h] in the usage.
To get 'help' for the subcommands:
1155:~/mypy$ python3 stack69633930-1.py -h
usage: example [-h] [-g] {a,b} ...
optional arguments:
-h, --help show this help message and exit
Global arguments:
-g, --global A global argument.
Available subcommands:
{a,b}
a help for subcommand a
b help for subcommand b
A very cool program
add
parser_a = subparsers.add_parser("a", help='help for subcommand a')
That works with your help-group as well.
===
The add_parser method handles the "help" keyword in a special way. "add_help" is passed on the ArgumentParser creation function. They have to very different functions.
def add_parser(self, name, **kwargs):
# set prog from the existing prefix
if kwargs.get('prog') is None:
kwargs['prog'] = '%s %s' % (self._prog_prefix, name)
aliases = kwargs.pop('aliases', ())
# create a pseudo-action to hold the choice help
if 'help' in kwargs:
help = kwargs.pop('help')
choice_action = self._ChoicesPseudoAction(name, aliases, help)
self._choices_actions.append(choice_action)
# create the parser and add it to the map
parser = self._parser_class(**kwargs)
self._name_parser_map[name] = parser
# make parser available under aliases also
for alias in aliases:
self._name_parser_map[alias] = parser
return parser

Python argparse - print help for subparsers? [duplicate]

If I create a subparser with a specific help string, this string is not displayed when the user runs myprog command --help:
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help="sub-command help")
parser_command = subparsers.add_parser("command", help="Issue a command")
parser.parse_args()
The top-level help shows this command subcommand with the description "Issue a command" alongside:
$ python prog.py --help
usage: prog.py [-h] {command} ...
positional arguments:
{command} sub-command help
command Issue a command
optional arguments:
-h, --help show this help message and exit
However the subcommand's help doesn't show this description:
$ python prog.py command --help
usage: prog.py command [-h]
optional arguments:
-h, --help show this help message and exit
What I would expect is for the subcommand's help to print out what the subcommand is actually for. I.e. I expected to see the text "Issue a command" somewhere in the output to python prog.py command --help.
Is there a way to include this text in the subcommand's help output? Is there another subparser attribute that can be used to provide a description of the subcommand?
The add_parser method accepts (most) of the parameters that a ArgumentParser constructor does.
https://docs.python.org/3/library/argparse.html#sub-commands
It's easy to overlook this sentence in the add_subparsers paragraph:
This object has a single method, add_parser(), which takes a command name and any ArgumentParser constructor arguments, and returns an ArgumentParser object that can be modified as usual.
In [93]: parser=argparse.ArgumentParser()
In [94]: sp = parser.add_subparsers(dest='cmd',description='subparses description')
In [95]: p1 = sp.add_parser('foo',help='foo help', description='subparser description')
In [96]: p1.add_argument('--bar');
help for the main parser:
In [97]: parser.parse_args('-h'.split())
usage: ipython3 [-h] {foo} ...
optional arguments:
-h, --help show this help message and exit
subcommands:
subparses description
{foo}
foo foo help
...
help for the subparser:
In [98]: parser.parse_args('foo -h'.split())
usage: ipython3 foo [-h] [--bar BAR]
subparser description
optional arguments:
-h, --help show this help message and exit
--bar BAR
...

Make a command-line-based application with python argparse only

I would want to make a command-line-based application using python and argparse library. But I am having some challenges with this library, This is the command looks like (the usage):
prog (foo|bar) [-v] (-h "key:value")* [-d inline-data] [-f file] URL
This is the prog help output:
> prog help
prog is a simple application
Usage:
prog command [arguments]
The commands are:
foo executes FOO
bar executes BAR
help prints this screen.
Use "prog help [command]" for more information about a command.
My challenge is in this part. I want to give separate help description when we execute the following commands:
prog help foo
prog help bar
import argparse
parser = argparse.ArgumentParser(prog='prog.py',description='some help', add_help=False)
#I turned off the default help, and defined -h separately.
parser.add_argument('-h', '--header', metavar='', help='headerHelpString')
subparsers = parser.add_subparsers(help='help sub-command')
subparserHelp = subparsers.add_parser('help', help='some help')
subparserFooBar = subparserHelp.add_argument('method', choices=['foo', 'bar'])
group = parser.add_mutually_exclusive_group()
group.add_argument('-q', '--quiet', action='store_true', help='print quiet')
group.add_argument('-v', '--verbose', action='store_true', help='print verbose')
args = parser.parse_args()
Without arguments, I get an error - it requires the subparsers command, 'help'. As defined there are a couple of flagged arguments for the toplevel. Arguments for the subparser are not displayed in the usage (or full help if it was enabled).
1327:~/mypy$ python stack46982125.py
usage: prog.py [-h] [-q | -v] {help} ...
prog.py: error: too few arguments
In Py3 subparsers are 'optional', showing what would be set:
1328:~/mypy$ python3 stack46982125.py
Namespace(header=None, quiet=False, verbose=False)
With the 'help' subparser, usage includes '[-h]', but this is the default help. The subparsers doesn't inherit the add_help parameter; you have to set that explicitly.
1328:~/mypy$ python3 stack46982125.py help
usage: prog.py help [-h] {foo,bar}
prog.py help: error: the following arguments are required: method
Taking advantage of that -h, I get the same usage with a fuller help.
1329:~/mypy$ python3 stack46982125.py help -h
usage: prog.py help [-h] {foo,bar}
positional arguments:
{foo,bar}
optional arguments:
-h, --help show this help message and exit
And if I also define the required 'foo/bar' it parses fine.
1329:~/mypy$ python3 stack46982125.py help foo
Namespace(header=None, method='foo', quiet=False, verbose=False)
If I add
parser.print_help()
subparserHelp.print_help()
I get the added output:
usage: prog.py [-h] [-q | -v] {help} ...
some help
positional arguments:
{help} help sub-command
help some help
optional arguments:
-h , --header headerHelpString
-q, --quiet print quiet
-v, --verbose print verbose
usage: prog.py help [-h] {foo,bar}
positional arguments:
{foo,bar}
optional arguments:
-h, --help show this help message and exit
If I add a dest parameter
subparsers = parser.add_subparsers(dest='cmd', help='help sub-command')
And make the prints conditional
if args.cmd == 'help':
parser.print_help()
subparserHelp.print_help()
No display of help (in py3) or error as above (py2)
1340:~/mypy$ python3 stack46982125.py
Namespace(cmd=None, header=None, quiet=False, verbose=False)
display of helps:
1341:~/mypy$ python3 stack46982125.py help foo
Namespace(cmd='help', header=None, method='foo', quiet=False, verbose=False)
....
If you turn off the -h help, then you have to somehow capture the 'help' string, and act on it with your own help or the print_help method. You can also define another flagged argument and give it an action='help' parameter.
Do you really need to turn turn off the default add_help. Isn't it simpler to use the default help approach? It's probably more familiar to your users.

Collapse multiple options with argparse

I'm looking for a classy way to collapse help messages for boolean toggles in
argparse. For example this:
import argparse
parser = argparse.ArgumentParser("booleans")
parser.add_argument('--no-store', action='store_false',
help="Don't do it'")
parser.add_argument('--store', action='store_true',
help="Do it")
parser.print_help()
prints:
usage: booleans [-h] [--no-store] [--store]
optional arguments:
-h, --help show this help message and exit
--no-store Don't do it'
--store Do it
But I've got a whole bunch of boolean flags, what I'd really like is to be able to write it in a way that makes it print:
usage: booleans [-h] [--[no-]store]
optional arguments:
-h, --help show this help message and exit
--[no-]store Do or don't do it.
Is there any nice way for me to collapse arguments, and provide custom help
text and option names?
You can specify the usage parameter to the ArgumentParser and write your own syntax line.
usage = ['booleans [-h]']
parser = argparse.ArgumentParser("booleans",usage=usage[0],
formatter_class=argparse.RawDescriptionHelpFormatter)
grp1 = parser.add_argument_group(title='Boolean arguments',description='')
def foo(group, name, help):
# create a pair of arguments, with one 'help' line
group.add_argument('--no-'+name, action='store_false', help=argparse.SUPPRESS)
group.add_argument('--'+name, action='store_true', help=argparse.SUPPRESS)
usage.append(' [--[no-]%s]'%name)
group.description += "[--[no-]%s]\t%s.\n"%(name,help)
foo(grp1, 'store', "Do or don't do it",)
foo(grp1, 'remove', "Do or don't do it",)
parser.usage = ''.join(usage)
parser.print_help()
produces
usage: booleans [-h] [--[no-]store] [--[no-]remove]
optional arguments:
-h, --help show this help message and exit
Boolean arguments:
[--[no-]store] Do or don't do it.
[--[no-]remove] Do or don't do it.
I use RawDescriptionHelpFormatter to allow use of\t. It would also allow a multiline description (other pairs of arguments).
With the SUPPRESS it really doesn't matter whether the arguments are in grp1 or parser.

In python's argparse module, how can I disable printing subcommand choices between curly brackets?

How can I disable printing subcommand choices, the ones between curly brackets? Using an example at http://docs.python.org/dev/library/argparse.html#sub-commands, the normal output is:
usage: [-h] {foo,bar} ...
optional arguments:
-h, --help show this help message and exit
subcommands:
{foo,bar} additional help
What I want is to print this:
usage: [-h] {foo,bar} ...
optional arguments:
-h, --help show this help message and exit
subcommands:
Removing just the last line.
To avoid spamming my users with the huge ugly curly-braced list of dozens of sub-commands, I simply set the metavar attribute of the subcommand object. My code looks like:
import argparse
parser = argparse.ArgumentParser(description='Stack Overflow example')
subs = parser.add_subparsers()
subs.metavar = 'subcommand'
sub = subs.add_parser('one', help='does something once')
sub = subs.add_parser('two', help='does something twice')
parser.parse_args()
And the output of running this script with a single -h argument is:
usage: tmp.py [-h] subcommand ...
Stack Overflow example
positional arguments:
subcommand
one does something once
two does something twice
optional arguments:
-h, --help show this help message and exit
The result is not exactly what you illustrate as your best desired case, but I think that it may be the closest you can get without subclassing argparse.ArgumentParser and overriding the things you need adjusted, which would be messy work.
Override ArgumentParser.print_usage() with your own method to print whatever, however you want. If all you want to do is eliminate the last line, call the original version, capture the results (by sending it to a file) and print just the part(s) you want.

Categories