Search code examples
pythonargparse

Argparse value for optional positional argument


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?


Solution

  • 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.

    • If -c val was passed, it will be present in args with value "val".
    • If -c was passed without value, it will be present in args with value None.
    • If -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)