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.
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>"')
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With