Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to make argument optional in python argparse

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.

like image 971
user1416227 Avatar asked Apr 02 '13 00:04

user1416227


2 Answers

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.

Example (with type string):

parser.add_argument('-o', '--outfile', nargs='?', const='arg_was_not_given', help='output file, in JSON format')  args = parser.parse_args()  if args.outfile is None:     print('Option not given at all') elif args.outfile == 'arg_was_not_given':     print('Option given, but no command-line argument: "-o"') elif:     print('Option and command-line argument given: "-o <file>"') 
like image 175
mhristache Avatar answered Sep 19 '22 23:09

mhristache


You specified a default argument for the outfile.

parser.add_argument('-o', '--outfile', nargs='?', type=argparse.FileType('w'), default='out.json', help='output file, in JSON format') 

If the -o option isn't specified at the command line, the arg parser inserts the default argument.

Change this to:

parser.add_argument('-o', '--outfile', nargs='?', type=argparse.FileType('w'), help='output file, in JSON format') 

and things should work as you expect.


If you want to be able to specify -o, without a filename, you probably want something like:

out_file = args.out if args.out is not None else 'json.out'

I'm not sure if the relevant parameter will be None or '' (i.e., empty string) if you specify the -o without a parameter--I suspect it's None, but I don't know for sure. You'll have to test it out to be sure.

I don't know how to do this without extra logic with argparse.

like image 27
BenDundee Avatar answered Sep 16 '22 23:09

BenDundee