Nilesh Bhave
Nilesh Bhave

Reputation: 301

Argument dependency in argparse

I have test scenario where I need to take action based on arguments passed to python script. The requirement is as follows:

test.py -a a1 -b b1 [[[-c c1] [-d d1]] | [-e e1 -f f1]]

  1. The user has to pass mandatory arguments '-a' and '-b'.
  2. The arguments '-c' and '-d' are optional arguments which could be passed individually.
  3. The arguments '-e' and '-f' are optional arguments that has to be passed together along with mandatory arguments.
  4. If any of '-c' or '-d' are passed along with '-e' and '-f', then script should give error for arguments '-c' or '-d'.
  5. If '-e' and '-f' are passed along with '-c' or '-d', then script should give error for arguments '-e' and '-d'.

The error could be generic error for all the optional arguments.

Till now, I'm trying to do it using parents= option of argparse.ArgumentParser but it is giving error when I try to read the passed arguments.

import argparse


parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument('-a', type=str, required=True)
parent_parser.add_argument('-b', type=str, required=True)

one_parser = argparse.ArgumentParser(parents=[parent_parser])
one_parser.add_argument('-c', type=str, default="test")
one_parser.add_argument('-d', type=str, default="Default")

multi_parser = argparse.ArgumentParser(parents=[parent_parser])
multi_parser.add_argument('-e', type=str)
multi_parser.add_argument('-f', type=str)

if str(one_parser.parse_args().c):
    print(one_parser.parse_args())
elif str(multi_parser.parse_args().e):
    print(multi_parser.parse_args())

When I run the script as :

test.py -a a1 -b b1 -e e1 -f f1

I'm getting the error :

usage: test.py [-h] -a A -b B [-c C] [-d D]
test.py: error: unrecognized arguments: -e e1 -f f1

Process finished with exit code 2

Any pointers are highly appreciated. Thank you.

Answer: The solution is hack but it worked for me.

import argparse


parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('-a', type=str, required=True)
parser.add_argument('-b', type=str, required=True)
parser.add_argument('-c', type=str)
parser.add_argument('-d', type=str)
parser.add_argument('-e', type=str)
parser.add_argument('-f', type=str)
args = parser.parse_args()

a = str(args.a)
b = str(args.b)
c = str(args.c)
d = str(args.d)
e = str(args.e)
f = str(args.f)

if (e != "None" and f == "None") or (e == "None" and f != "None"):
    if c == "None" and d == "None":
        raise argparse.ArgumentTypeError("Both e and f arguments are required")
    elif c != "None" or d != "None":
        raise argparse.ArgumentTypeError("e and f are not supported with c or d")
if (c != "None" or d != "None") and (e != "None" or f != "None"):
    raise argparse.ArgumentTypeError("c and d are not supported with e and f")
if e == "None" and f == "None":
    if c == "None":
        c = "test"
    if d == "None":
        d = "Default"

IMO, argparser should handle argument dependency more efficiently. Maybe someone will contribute and help us all.. :)

Upvotes: 2

Views: 1766

Answers (1)

Valentin M.
Valentin M.

Reputation: 520

Your issue is using multiple parser, arguments groups might help you.

You then have to check yourself that arguments comply with your conditions, and raise argparse.ArgumentError when necessary.

Here is a code sample that could do the trick. The arguments_group serve only to document the -h parameter (argparser help)

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("-a", required=True)
parser.add_argument("-b", required=True)
first = parser.add_argument_group("group 1", "None, one or both")
first.add_argument("-c")
first.add_argument("-d")
second = parser.add_argument_group("group 2", "None or both")
second.add_argument("-e")
second.add_argument("-f")

args = parser.parse_args()

a = args.a
b = args.b
c = args.c
d = args.d
e = args.e
f = args.f

if (e is not None and f is None) or (e is None and f is not None):
    if c is None and d is None:
        raise argparse.ArgumentError("Both e and f arguments are required")
    elif c is None or d is not None:
        raise argparse.ArgumentError("e and f are not supported with c or d")
if (c is not None or d is not None) and (e is not None or f is not None):
    raise argparse.ArgumentError("c and d are not supported with e and f")

Upvotes: 1

Related Questions