Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manage precedence in argparse?

I'm using argparse to manage command line options, and I want manage two options, --check and --nocheck. I'm actually doing something like this in mybadscript.py:

[...]
args = parser.parse_args()

if args.check:
    check = True

if args.nocheck:
    check = False
[...]

The problem is that if launch the script this way:

python mybadscript.py --nocheck --check

check will be set to False. This is incorrect, since the last option is --check.

How can I correctly manage them?

PS: I know you can easily avoid it using only one option, but I would know how I can manage option precedence with argparse, since you can encounter it in more complicated scenarios.

PPS: the suggested answer does incidentally answer my question, but the related question is not the same one.

like image 868
Marco Sulla Avatar asked Sep 17 '15 16:09

Marco Sulla


2 Answers

See the argparse documentation. Here's an example of what you might want. I've included several options you may not need—I thought that was better than leaving them off and you needing them.

>>> parser = argparse.ArgumentParser()
>>> group = parser.add_mutually_exclusive_group(required=True)
>>> group.add_argument('--check', action='store_true', dest="check")
>>> group.add_argument('--nocheck', action='store_false', dest="check")
>>> parser.parse_args(["--check"])
Namespace(check=True)
>>> parser.parse_args(["--nocheck"])
Namespace(check=False)

You may not want the mutually exclusive part—in that case delete the second line and replace group with parser. If you do so you may wish to add this line:

>>> parser.set_defaults(check=False)
like image 97
robert Avatar answered Nov 07 '22 21:11

robert


You have two arguments that independently set two different Namespace attributes, args.check and args.nocheck. With --nocheck --check input line, both are set.

In:

if args.check:
    check = True
if args.nocheck:
    check = False

you test nocheck last, so that one overrides check.

argparse tries to handle optionals (flagged arguments) in an order independent manner.

If they write to the same 'dest', it's the last one that you will end up seeing. One way to see what was entered, and in what order, is to use an 'append' action:

parser=argparse.ArgumentParser()
parser.add_argument('--check', dest='checked', action='append_const', const=True)
 parser.add_argument('--nocheck', dest='checked', action='append_const', const=False)

In [14]: parser.parse_args(''.split()) # no arg
Out[14]: Namespace(checked=None)

I could have made the default []

In [15]: parser.parse_args('--check'.split())
Out[15]: Namespace(checked=[True])

In [16]: parser.parse_args('--nocheck'.split())
Out[16]: Namespace(checked=[False])

In [17]: parser.parse_args('--nocheck --check --nocheck'.split())
Out[17]: Namespace(checked=[False, True, False])

The paired 'store_true' and 'store_false' in the other answer is simpler.

Better yet, choose one as the default choice, and just provide an option for the other. As long as the default is clear, there really isn't a need for both arguments.

like image 37
hpaulj Avatar answered Nov 07 '22 19:11

hpaulj