0

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
1
  • One positional is allowed in a mutually_exclusive_group, but it must be optional, that is, have an nargs of "?" or "*". And you should provide a default with it. A flagged --filter with nargs=3 is a simpler idea. Commented Nov 9, 2022 at 20:52

2 Answers 2

2

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.

Filters as optional

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

Own parsing

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.