Reputation: 8081
I am trying to create an argparse optional argument that may -OR- may not have an associated input value. I want the following behavior:
1. argument not specified, value=None
2. argument specified with a value, value=user_input
3. argument specified without a value, value=derived from a positional value
The first 2 are easy. It's the third one I can't figure out. I found 2 posts that do something similar:
python argparse optional positional argument with detectable switch
This one sets the default value to a constant for an optional argument without a value.
Argparse optional positional arguments?
This topic is close, but not quite what I need either (he derives the default value from a system call):
I want mine to be a determined from an positional value.
Simple example code I created:
parser = argparse.ArgumentParser()
parser.add_argument('input')
parser.add_argument('-c', '--csv', nargs='?')
parser.add_argument('-p', '--pnf', nargs='?')
When I set the input and print:
args = parser.parse_args('my.h5 -c my_file.csv --pnf'.split())
print ('Input = %s' % args.input)
print ('CSV file = %s' % args.csv)
print ('PNF file = %s' % args.pnf)
I get:
Input = my.h5
CSV file = my_file.csv
PNF file = None
If I modify my input to:
args = parser.parse_args('my.h5 -c'.split())
The resulting output is:
Input = my.h5
CSV file = None
PNF file = None
When Value = None
, I can't tell if the optional argument was not defined, or was defined but without a value. In the second case, I want to derive the CSV File name from the positional argument (in this example the derived name would be my.csv
). I want to do the same when --pnf is defined (where default PNF would be my.pnf
for above). Is there a way to do this?
Upvotes: 1
Views: 3472
Reputation: 363456
I can't tell if the optional argument was not defined, or was defined but without a value
If you create your parser like this:
parser = argparse.ArgumentParser(argument_default=argparse.SUPPRESS)
Now it is possible to distinguish between the three cases.
-c val
was passed, it will be present in args
with value "val"
.-c
was passed without value, it will be present in args
with value None
.-c
was omitted entirely, it won't be present in args
.The same goes for -p
.
If you only want this suppression to apply for one option, rather than on the whole parser, that is possible too:
parser.add_argument('-c', '--csv', nargs='?', default=argparse.SUPPRESS)
Upvotes: 3
Reputation: 8081
This shows the logic I implemented using default=False, const=True
, based on earlier comments from @hpaulj.
parser = argparse.ArgumentParser()
parser.add_argument('input')
parser.add_argument('-c', '--csv', nargs='?', default=False, const=True)
parser.add_argument('-p', '--pnf', nargs='?', default=False, const=True)
args = parser.parse_args('my.h5 -c'.split())
print(vars(args))
HDF5_FILE = args.input
if isinstance(args.csv, str) :
CSV_FILE = args.csv
elif args.csv :
CSV_FILE=HDF5_FILE[:-3] + '_v3_stress.csv'
else :
CSV_FILE = ''
# repeat above logic for args.pnf
print ('input=', HDF5_FILE, ', csv=', CSV_FILE, ' pnf=', PNF_FILE )
Resulting output looks like this:
{'input': 'my.h5', 'csv': True, 'pnf': False}
hdf5= my.h5 , csv= my_v3_stress.csv pnf=
Modified 'parse_args()' and resulting output:
args = parser.parse_args('my.h5 -c my_file.csv -p'.split())
Gives:
{'input': 'my.h5', 'csv': 'my_file.csv', 'pnf': True}
hdf5= my.h5 , csv= my_file.csv pnf= my_v3_stress.nrf
If I had a lot of variables to check, I would move the if/elif/else
logic to a def
that returns the desired value.
Upvotes: 0
Reputation: 8081
For completeness (and future reference), I am posting the modified code required to get arguments when using argument_default=argparse.SUPPRESS
. See below:
parser = argparse.ArgumentParser(argument_default=argparse.SUPPRESS)
parser.add_argument('input')
parser.add_argument('-c', '--csv', nargs='?')
parser.add_argument('-p', '--pnf', nargs='?')
args = parser.parse_args('my.h5 -c my_file --pnf'.split())
for d_key, d_val in vars(args).items() :
print (d_key, d_val)
Results in this output:
input my.h5
csv my_file
pnf None
For the second set of inputs
args = parser.parse_args('my.h5 -c'.split())
print(vars(args))
for d_key, d_val in vars(args).items() :
print (d_key, d_val)
Output looks like this:
input my.h5
csv None
Upvotes: 0
Reputation: 231665
Making use of the const
parameter:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('input')
parser.add_argument('-c', '--csv', nargs='?', const='foobar')
parser.add_argument('-p', '--pnf', nargs='?', const='foobar')
args = parser.parse_args()
print(args)
if args.csv and args.csv=='foobar':
args.csv = args.input
args.pnf = args.input if (args.pnf and args.pnf=='foobar') else args.pnf
print(args)
Your two sample inputs:
0933:~/mypy$ python3 stack53228663.py my.h5 -c my_file.csv --pnf
Namespace(csv='my_file.csv', input='my.h5', pnf='foobar')
Namespace(csv='my_file.csv', input='my.h5', pnf='my.h5')
0933:~/mypy$ python3 stack53228663.py my.h5 -c
Namespace(csv='foobar', input='my.h5', pnf=None)
Namespace(csv='my.h5', input='my.h5', pnf=None)
Upvotes: 0