how to make argument optional in python argparse

user1416227 picture user1416227 · Apr 2, 2013 · Viewed 30.5k times · Source

I would like to make these invocations of myprog work, and no others.

$ python3 myprog.py -i infile -o outfile
$ python3 myprog.py -o outfile
$ python3 myprog.py -o
$ python3 myprog.py 

In particular I want to make it illegal to specify the infile but not the outfile.

In the third case, a default name for the outfile is assumed, "out.json." In the second, third and fourth cases, a default name for the input file is assumed, "file.n.json", where n is an integer version number. In the fourth case the output file would be "file.n+1.json" where n+1 is a version number one larger than the one on the input file. The relevant section of my code is:

import argparse

parser = argparse.ArgumentParser(description="first python version")
parser.add_argument('-i', '--infile', nargs=1, type=argparse.FileType('r'), help='input file, in JSON format')
parser.add_argument('-o', '--outfile', nargs='?', type=argparse.FileType('w'), default='out.json', help='output file, in JSON format')

args = parser.parse_args()

print("Here's what we saw on the command line: ")
print("args.infile",args.infile)
print("args.outfile",args.outfile)

if args.infile and not args.outfile:
    parser.error("dont specify an infile without specifying an outfile")
elif not args.infile:
    print("fetching infile")
else: # neither was specified on the command line
    print("fetching both infile and outfile")

Problem is, when I run

$ python3 myprog.py -i infile.json

instead of the parser error I hoped for, I get:

Here's what we saw on the command line: 
args.infile [<_io.TextIOWrapper name='infile.json' mode='r' encoding='UTF-8'>]
args.outfile <_io.TextIOWrapper name='out.json' mode='w' encoding='UTF-8'>
fetching both infile and outfile

...which suggests that even though there was no "-o" on the command line it acted as if there was.

Answer

mhristache picture mhristache · Jan 6, 2014

As an add-on to the selected answer:

The option to run -o without specifying a file, can be done using const combined with nargs='?'.

From the docs:

When add_argument() is called with option strings (like -f or --foo) and nargs='?'. This creates an optional argument that can be followed by zero or one command-line arguments. When parsing the command line, if the option string is encountered with no command-line argument following it, the value of const will be assumed instead. See the nargs description for examples.