Reputation: 187
I have the following code:
parser = argparse.ArgumentParser(description='')
parser.add_argument('-l', '--login', action='store_true')
parser.add_argument('FILTER1')
parser.add_argument('FILTER2')
parser.add_argument('FILTER3')
I'd like "--login" to be mutually exclusive from FILTER1, FILTER2, FILTER3. Also, FILTER1, FILTER2, FILTER3 are all required when any of the 3 are mentioned.
So either:
cli FILTER1 FILTER2 FILTER3
OR
cli --login
Upvotes: 0
Views: 1169
Reputation: 10452
You could use ArgumentParser.add_mutually_exclusive_group
, but it requires you to use optional arguments.
parser = argparse.ArgumentParser(description='')
group = parser.add_mutually_exclusive_group()
group.add_argument('-l', '--login', action='store_true')
group.add_argument('-f', '--filter', nargs=3)
Pass required=True
to add_mutually_exclusive_group
if you want either --login
or --filter
to be required.
Upvotes: 0
Reputation: 32964
The most straightforward way to do this with argparse is to make the filters into an optional argument followed by nargs=3
in a mutually exclusive group with "login". Otherwise, you could do your own parsing and make each filter an optional positional.
import argparse
parser = argparse.ArgumentParser()
meg = parser.add_mutually_exclusive_group()
meg.add_argument('-l', '--login', action='store_true')
meg.add_argument('-f', '--filters', nargs=3, metavar=('FILTER1', 'FILTER2', 'FILTER3'))
for arg_string in '-l', '-f 1 2 3', '-l -f 4 5 6', '-f 8 9':
args = arg_string.split()
try: # This try-block for demo
ns = parser.parse_args(args)
except SystemExit:
pass
else:
print(ns)
print()
Output:
Namespace(login=True, filters=None)
Namespace(login=False, filters=['1', '2', '3'])
usage: tmp.py [-h] [-l | -f FILTER1 FILTER2 FILTER3]
tmp.py: error: argument -f/--filters: not allowed with argument -l/--login
usage: tmp.py [-h] [-l | -f FILTER1 FILTER2 FILTER3]
tmp.py: error: argument -f/--filters: expected 3 arguments
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-l', '--login', action='store_true')
parser.add_argument('FILTER1', nargs='?')
parser.add_argument('FILTER2', nargs='?')
parser.add_argument('FILTER3', nargs='?')
for arg_string in '-l', '1 2 3', '-l 4 5 6', '8 9':
args = arg_string.split()
ns = parser.parse_args(args)
print(ns)
try: # This try-block for demo
if ns.login and ns.FILTER1 is not None:
parser.error("filters not allowed with argument -l/--login")
elif not ns.login and any(a is None for a in [ns.FILTER1, ns.FILTER2, ns.FILTER3]):
parser.error("Expected three filters")
except SystemExit:
pass
print()
Output:
Namespace(login=True, FILTER1=None, FILTER2=None, FILTER3=None)
Namespace(login=False, FILTER1='1', FILTER2='2', FILTER3='3')
Namespace(login=True, FILTER1='4', FILTER2='5', FILTER3='6')
usage: tmp.py [-h] [-l] [FILTER1] [FILTER2] [FILTER3]
tmp.py: error: filters not allowed with argument -l/--login
Namespace(login=False, FILTER1='8', FILTER2='9', FILTER3=None)
usage: tmp.py [-h] [-l] [FILTER1] [FILTER2] [FILTER3]
tmp.py: error: Expected three filters
You should also document this behaviour since argparse won't do it for you. You might also want to change the parser.usage
. This can be a pain, so that's why I recommend the first option personally.
Upvotes: 2