In Python argparse, is it possible to have paired --no-something/--something arguments? In Python argparse, is it possible to have paired --no-something/--something arguments? python-3.x python-3.x

In Python argparse, is it possible to have paired --no-something/--something arguments?


Well, none of the answers so far are quite satisfactory for a variety of reasons. So here is my own answer:

class ActionNoYes(argparse.Action):    def __init__(self, opt_name, dest, default=True, required=False, help=None):        super(ActionNoYes, self).__init__(['--' + opt_name, '--no-' + opt_name], dest, nargs=0, const=None, default=default, required=required, help=help)    def __call__(self, parser, namespace, values, option_string=None):        if option_string.starts_with('--no-'):            setattr(namespace, self.dest, False)        else:            setattr(namespace, self.dest, True)

And an example of use:

>>> p = argparse.ArgumentParser()>>> p._add_action(ActionNoYes('foo', 'foo', help="Do (or do not) foo. (default do)"))ActionNoYes(option_strings=['--foo', '--no-foo'], dest='foo', nargs=0, const=None, default=True, type=None, choices=None, help='Do (or do not) foo. (default do)', metavar=None)>>> p.parse_args(['--no-foo', '--foo', '--no-foo'])Namespace(foo=False)>>> p.print_help()usage: -c [-h] [--foo]optional arguments:  -h, --help       show this help message and exit  --foo, --no-foo  Do (or do not) foo. (default do)

Unfortunately, the _add_action member function isn't documented, so this isn't 'official' in terms of being supported by the API. Also, Action is mainly a holder class. It has very little behavior on its own. It would be nice if it were possible to use it to customize the help message a bit more. For example saying --[no-]foo at the beginning. But that part is auto-generated by stuff outside the Action class.


I modified the solution of @Omnifarious to make it more like the standard actions:

import argparseclass ActionNoYes(argparse.Action):    def __init__(self, option_strings, dest, default=None, required=False, help=None):        if default is None:            raise ValueError('You must provide a default with Yes/No action')        if len(option_strings)!=1:            raise ValueError('Only single argument is allowed with YesNo action')        opt = option_strings[0]        if not opt.startswith('--'):            raise ValueError('Yes/No arguments must be prefixed with --')        opt = opt[2:]        opts = ['--' + opt, '--no-' + opt]        super(ActionNoYes, self).__init__(opts, dest, nargs=0, const=None,                                           default=default, required=required, help=help)    def __call__(self, parser, namespace, values, option_strings=None):        if option_strings.startswith('--no-'):            setattr(namespace, self.dest, False)        else:            setattr(namespace, self.dest, True)

You can add the Yes/No argument as you would add any standard option. You just need to pass ActionNoYes class in the action argument:

parser = argparse.ArgumentParser()parser.add_argument('--foo', action=ActionNoYes, default=False)

Now when you call it:

>> args = parser.parse_args(['--foo'])Namespace(foo=True)>> args = parser.parse_args(['--no-foo'])Namespace(foo=False)>> args = parser.parse_args([])Namespace(foo=False)  


Does the add_mutually_exclusive_group() of argparse help?

parser = argparse.ArgumentParser()exclusive_grp = parser.add_mutually_exclusive_group()exclusive_grp.add_argument('--foo', action='store_true', help='do foo')exclusive_grp.add_argument('--no-foo', action='store_true', help='do not do foo')args = parser.parse_args()print 'Starting program', 'with' if args.foo else 'without', 'foo'print 'Starting program', 'with' if args.no_foo else 'without', 'no_foo'

Here's how it looks when run:

./so.py --helpusage: so.py [-h] [--foo | --no-foo]optional arguments:  -h, --help  show this help message and exit  --foo       do foo  --no-foo    do not do foo./so.pyStarting program without fooStarting program without no_foo./so.py --no-foo --foousage: so.py [-h] [--foo | --no-foo]so.py: error: argument --foo: not allowed with argument --no-foo

This is different from the following in the mutually exclusive group allows neither option in your program (and I'm assuming that you want options because of the -- syntax). This implies one or the other:

parser.add_argument('--foo=', choices=('y', 'n'), default='y',                    help="Do foo? (default y)")

If these are required (non-optional), maybe using add_subparsers() is what you're looking for.

Update 1

Logically different, but maybe cleaner:

...exclusive_grp.add_argument('--foo', action='store_true', dest='foo', help='do foo')exclusive_grp.add_argument('--no-foo', action='store_false', dest='foo', help='do not do foo')args = parser.parse_args()print 'Starting program', 'with' if args.foo else 'without', 'foo'

And running it:

./so.py --fooStarting program with foo./so.py --no-fooStarting program without foo./so.pyStarting program without foo