For parsing boolean command-line options using Python's built-in argparse
package, I am aware of this question and its several answers: Parsing boolean values with argparse.
Several of the answers (correctly, IMO) point out that the most common and straightforward idiom for boolean options (from the caller's point of view) is to accept both --foo
and --no-foo
options, which sets some value in the program to True
or False
, respectively.
However, all the answers I can find don't actually accomplish the task correctly, it seems to me. They seem to generally fall short on one of the following:
- A suitable default can be set (
True
,False
, orNone
). - Help text given for
program.py --help
is correct and helpful, including showing what the default is. - Either of (I don't really care which, but both are sometimes desirable):
- An argument
--foo
can be overridden by a later argument--no-foo
and vice versa; --foo
and--no-foo
are incompatible and mutually exclusive.
- An argument
What I'm wondering is whether this is even possible at all using argparse
.
Here's the closest I've come, based on answers by @mgilson and @fnkr:
def add_bool_arg(parser, name, help_true, help_false, default=None, exclusive=True):
if exclusive:
group = parser.add_mutually_exclusive_group(required=False)
else:
group = parser
group.add_argument('--' + name, dest=name, action='store_true', help=help_true)
group.add_argument('--no-' + name, dest=name, action='store_false', help=help_false)
parser.set_defaults(**{name: default})
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
add_bool_arg(parser, 'foo', "Do foo", "Don't foo", exclusive=True)
add_bool_arg(parser, 'bar', "Do bar", "Don't bar", default=True, exclusive=False)
That does most things well, but the help-text is confusing:
usage: argtest.py [-h] [--foo | --no-foo] [--bar] [--no-bar]
optional arguments:
-h, --help show this help message and exit
--foo Do foo (default: None)
--no-foo Don't foo (default: None)
--bar Do bar (default: True)
--no-bar Don't bar (default: True)
A better help text would be something like this:
usage: argtest.py [-h] [--foo | --no-foo] [--bar] [--no-bar]
optional arguments:
-h, --help show this help message and exit
--foo --no-foo Whether to foo (default: None)
--bar --no-bar Whether to bar (default: True)
But I don't see a way to accomplish that, since "--*" and "--no-*" must always be declared as separate arguments (right?).
In addition to the suggestions at the SO question mentioned above, I've also tried creating a custom action using techniques shown in this other SO question: Python argparse custom actions with additional arguments passed . These fail immediately saying either "error: argument --foo: expected one argument"
, or (if I set nargs=0
) "ValueError: nargs for store actions must be > 0"
. From poking into the argparse
source, it looks like this is because actions other than the pre-defined 'store_const', 'store_true', 'append', etc. must use the _StoreAction
class, which requires an argument.
Is there some other way to accomplish this? If someone has a combination of ideas I haven't thought of yet, please let me know!
(BTW- I'm creating this new question, rather than trying to add to the first question above, because the original question above was actually asking for a method to handle --foo TRUE
and --foo FALSE
arguments, which is different and IMO less commonly seen.)