More nuanced help messages

14 views
Skip to first unread message

Jeff

unread,
Nov 11, 2010, 9:34:18 AM11/11/10
to argparse-users
I am the main developer for a command-line tool that automates the
plotting of histograms, allowing you to specify all sorts of options
like locations of a legend, styles of plots, colors, scalings, etc.
The number of options is starting to get pretty unwieldy, so I am
looking to better organize them so it's not overwhelming.

Particularly, I thought it would be nice if I could get help messages
per command. For example, I could have a --label command with a help
message like this:

--label LABEL Add text to the figure. Call 'myprog --label -h'
for more options.

Then calling 'myprog --label -h' would let you know about other
options:

--label LABEL Add text to the figure. Call 'myprog --label -h'
for more options.
--label-top LABEL Add text to the top of the figure.
--label-bottom LABEL Add text to the bottom of the figure.
--label-size SIZE The font size (in points) of the label text

So, I would be essentially hiding all of those extra options from the
standard help menu, and letting the user decide when they need the
greater detail.

Any thoughts on how I might implement this, or is it beyond the scope
of what argparse could handle?

Steven Bethard

unread,
Nov 11, 2010, 10:51:28 AM11/11/10
to argpars...@googlegroups.com

You can silence help from arguments and groups using
argparse.SUPPRESS. However, there isn't a publicly documented way to
do the kind of introspection you'd probably need to get this to work.
However, if you don't mind using a few implementation details that
could change in the future, you could do something like:

class StoreOrSilenceAction(argparse.Action):
def __init__(self, group, **kwargs):
super(StoreOrSilenceAction, self).__init__(**kwargs)
self.group = group
def __call__(self, parser, namespace, value, option_string=None):
if value is None:
for action in parser._actions:
if action not in self.group._group_actions:
action.help = argparse.SUPPRESS
else:
setattr(namespace, self.dest, value)

parser = argparse.ArgumentParser()
a_group = parser.add_argument_group('a')
a_group.add_argument('-a', nargs='?', action=StoreOrSilenceAction,
group=a_group)
b_group = parser.add_argument_group('b')
b_group.add_argument('-b', nargs='?', action=StoreOrSilenceAction,
group=b_group)
args = parser.parse_args()

Basically, you make your options like --label take nargs='?', where
when there's no value, they silence the help for other things. You'd
probably need to do some extra error checking to make sure your user
didn't specify --label with no value but didn't ask for --help, but
maybe the above can at least get you started.

I guess this is probably a reason to add something like
parser.get_actions() and group.get_actions() so you wouldn't have to
rely on undocumented implementation details to do this kind of thing.
If you find that's the case, please feel free to file a feature
request on bugs.python.org.

Steve
--
Where did you get that preposterous hypothesis?
Did Steve tell you that?
        --- The Hiphopopotamus

Jeff

unread,
Nov 11, 2010, 1:57:20 PM11/11/10
to argparse-users
Your solution looks quite promising. I am already packaging argparse
into the module, so it's not too bad if I use some of the
implementation details and lock myself into one particular version.
Further, I think I could simplify the process by adding a 'parent'
attribute to the _ActionsContainer class rather than making a new
action. If users call 'prog -h' for a general help message, then they
would see every option that has None for parent. If users call 'prog
--label -h', they would only see the messages for '--label' and for
any other options that have '--label' as a parent.

In any case, I would need to modify what happens when '-h' is called.
How can I override the default behavior and call my own code for the '-
h' and '--help' options?

Thanks,
Jeff

Jeff

unread,
Nov 11, 2010, 4:24:13 PM11/11/10
to argparse-users
I found the add_help=False option to turn off the automatic '--help'
and -h' options and developed a working prototype for what I'd like to
do. I didn't mess with the actual argparse code, although the
solution still relies on the _actions member.

In this solution, I keep a dictionary of suboption_groups where the
key is the name of the main option which is displayed in the normal
help menu and the value is a list of the suboptions. Then, after
calling parse_args, I set the SUPPRESS flag for the desired options
and manually call print_help():

import argparse

suboption_groups = {}

parser = argparse.ArgumentParser(add_help=False,
usage="%(prog)s [options]")
parser.add_argument('--goodbye', help='help with goodbyes', nargs='?')
suboption_groups['hello'] = ['hello_loud', 'hello_soft']
parser.add_argument('--hello', const='', help='some help I am',
nargs='?')
parser.add_argument('--hello-loud', help='some help I am'.upper(),
nargs='?')
parser.add_argument('--hello-soft', help='some help I am'.lower(),
nargs='?')
parser.add_argument('-h', '--help', action='store_true',
help='show this message')

args = parser.parse_args()
if args.help:
suppressed_args = []
for group, subopts in suboption_groups.items():
suppressed_args += subopts
## if an argument has been specified, only show help for that
group
if ([getattr(args, x) is not None for x in [group] +
subopts]):
for action in parser._actions:
if action.dest not in [group] + subopts:
action.help = argparse.SUPPRESS
break
else:
for action in parser._actions:
## suppress only sub-options
if action.dest in suppressed_args:
action.help = argparse.SUPPRESS
parser.print_help()

$ python parsetest.py -h
usage: parsetest.py [options]

optional arguments:
--hello [HELLO] some help I am
--hello-loud [HELLO_LOUD]
SOME HELP I AM
--hello-soft [HELLO_SOFT]
some help i am

$ python parsetest.py --hello -h
usage: parsetest.py [options]

optional arguments:
--hello [HELLO] some help I am
--hello-loud [HELLO_LOUD]
SOME HELP I AM
--hello-soft [HELLO_SOFT]
some help i am

Steven Bethard

unread,
Nov 12, 2010, 4:30:33 AM11/12/10
to argpars...@googlegroups.com
On Thu, Nov 11, 2010 at 10:24 PM, Jeff <jeff....@gmail.com> wrote:
> I found the add_help=False option to turn off the automatic '--help'
> and -h' options and developed a working prototype for what I'd like to
> do.  I didn't mess with the actual argparse code, although the
> solution still relies on the _actions member.
>
> In this solution, I keep a dictionary of suboption_groups where the
> key is the name of the main option which is displayed in the normal
> help menu and the value is a list of the suboptions.  Then, after
> calling parse_args, I set the SUPPRESS flag for the desired options
> and manually call print_help():

Yep, that looks like about what I would have done. Glad you got it working!

Reply all
Reply to author
Forward
0 new messages