In my Python script, I have a lot of command line arguments. Some of them are mandatory while others are optional. Some of the optional are mandatory for certain values of mandatory params as well. What is the best way to take in such a case of command line arguments. I am using argparse.Argumentparser
for my purpose.
To clarify the use case, here is an example. Let's say I have 3 mandatory params: mode, foo, bar
and 5 optional params: op1, op2, op3, op4, op5
. Lets say, mode
can have 3 values: m1, m2, m3
. If mode
is m1
, op1 and op2
are mandatory. If mode
is m2
, op3
is mandatory and if mode
is m3
, op4 and op5
are mandatory.
What is the best way to put it into code? What I'm doing now seems too exhaustive and just feels that there has to be better ways to do this.
-> Current code
import argparse
# create and execute parser
my_parser = argparse.ArgumentParser(description="Demo")
my_parser.add_argument("--mode", action="store", type=str, required=True)
my_parser.add_argument("--foo", action="store", type=str, required=True)
my_parser.add_argument("--bar", action="store", type=str, required=True)
my_parser.add_argument("--op1", action="store", type=str, required=False, default=None)
my_parser.add_argument("--op2", action="store", type=str, required=False, default=None)
my_parser.add_argument("--op3", action="store", type=str, required=False, default=None)
my_parser.add_argument("--op4", action="store", type=str, required=False, default=None)
my_parser.add_argument("--op5", action="store", type=str, required=False, default=None)
args = my_parser.parse_args()
try:
if args.mode == "m1":
op1 = args.op1.lower()
op2 = args.op2.lower()
if args.mode == "m2":
op3 = args.op3.split(',')
if args.mode == "m3":
op4 = args.op4.lower()
op5 = args.op5.lower()
except AttributeError:
print("Wrong mode set for optional arguments")
exit()
As you can see, even this example just "looks" aesthetically bad (esp try
block parsing) and I have a lot many optional arguments to deal with in my case. Any help will be much appreciated.
Also, this approach doesn't exactly force the user to enter, eg, op1 and op2
in case his mode=m1
. If it is possible to do something like this as well, it would be bonus benefits for me.
Thanks.
CodePudding user response:
It seems like your program is handling multiple different tasks. The way to support this is to split up your parser into sub-commands:
import argparse
parser = argparse.ArgumentParser(description="Demo")
parser.add_argument("--foo", required=True)
parser.add_argument("--bar", required=True)
subparsers = parser.add_subparsers(dest='mode', required=True)
m1_parser = subparsers.add_parser('m1', help='mode 1')
m1_parser.add_argument("--op1", required=True)
m1_parser.add_argument("--op2", required=True)
m1_parser.add_argument("--op3")
m1_parser.add_argument("--op4")
m1_parser.add_argument("--op5")
m2_parser = subparsers.add_parser('m2', help='mode 2')
m2_parser.add_argument("--op1")
m2_parser.add_argument("--op2")
m2_parser.add_argument("--op3", required=True)
m2_parser.add_argument("--op4")
m2_parser.add_argument("--op5")
m3_parser = subparsers.add_parser('m3', help='mode 3')
m3_parser.add_argument("--op1")
m3_parser.add_argument("--op2")
m3_parser.add_argument("--op3")
m3_parser.add_argument("--op4", required=True)
m3_parser.add_argument("--op5", required=True)
args = parser.parse_args()
if args.mode == 'm1':
print(args.op1) # it will always exist
Usage:
$ python3 args.py m1
usage: args.py m1 [-h] --op1 OP1 --op2 OP2 [--op3 OP3] [--op4 OP4] [--op5 OP5]
args.py m1: error: the following arguments are required: --op1, --op2
While this seems repetitive, you now have the option of giving better names to the various options that are specific to the sub-commands.