Reputation: 79
I have read through a bunch of the questions already answered, but I don't see this covered--at least not that I recognized.
I am using argparse to take a file and convert it to a different type. The input filename is required. The output filename is NOT required as the optional argument should handle that. Here is the code so far:
import sys
import argparse
parser = argparse.ArgumentParser(description='Convert file to new type')
parser.add_argument('--json', type=str, help='Converts to json format')
parser.add_argument('--bibtex', type=str, help='Converts to bibtex format')
parser.add_argument('--html', type=str, help='Converts to html format')
parser.add_argument('inputfilename', type=str, help='enter the original filename')
args = parser.parse_args()
filename=args.filename
if args.json:
print('Converting to json ...')
#conversion code here
elif args.bibtex:
print('Converting to bibtex ...')
#conversion code here
elif args.html:
print('Converting to html ...')
#conversion code here
else:
print('No conversion type indicated')
The problem is that whenever I use one of these flags. If I do
$ ./orsconvert.py --json inputfilename
I get an error saying
orsconvert.py: error: the following arguments are required: inputfilename
It does this because it interprets the provided inputfilename
as an outputfilename connected to the --json
. It is trying to force me to actually state the output filename after the optional argument and before the input filename, similar to this:
$ ./orsconvert.py --json outputfilename inputfileneame
However, I do not want to do that if there is a way around it. I want it to accept --json
as the indication of what to do and then use inputfilename
as the input and automatically save it as outputfilename
according to what the code will specify.
Yes, I am looking at making the 3 optional arguments a group to simplify the code ... but that still doesn't explain how to get it to not require another argument after the optional one and before the final inputfilename
required argument.
Is there something else I can put in the parser.add_argument('...')
field that will stop requiring me to specify an outputfilename
? I would like to change the code as little as possible because I am already pushing the limits of my coding comprehension.
Upvotes: 1
Views: 887
Reputation: 530960
The problem is that you defined --json
to require an argument, and it's taking the input file name as that argument. Your three converters should use the store_true
action, not the default store
action.
parser = argparse.ArgumentParser(description='Convert file to new type')
parser.add_argument('--json', action='store_true', help='Converts to json format')
parser.add_argument('--bibtex', action='store_true', help='Converts to bibtex format')
parser.add_argument('--html', action='store_true', help='Converts to html format')
parser.add_argument('inputfilename', help='enter the original filename')
args = parser.parse_args()
With this change, args.json
et all are the Boolean values you expect them to be. The store_true
action takes care of defining the type to be bool
and the default value to be false
.
Better yet, though, would be a single required positional argument that must take the value json
, bibtex
, or html
. Then parse_args
itself would detect if an incorrect or missing conversion type was given.
parser = argparse.ArgumentParser(description='Convert file to new type')
parser.add_argument('inputfilename', help='enter the original filename')
parser.add_argument('conversion_type', choices=['json', 'bibtex', 'html'])
args = parser.parse_args()
filename = args.filename
conversion_type = args.conversion_type # Guaranteed to be json, html, or bibtex
if conversion_type == "json":
...
elif conversion_type == "bibtex":
...
elif conversion_type == "html":
If you would like an option with a default value, buckle up :) We're going to add 4 options: one that lets specify the output type explicitly, and three shortcuts for setting it.
p = argparse.ArgumentParser()
p.add_argument('inputfilename')
p.add_argument('--type', choices=['json', 'bibtex', 'html'], default='json')
p.add_argument('--json', action='store_const', const='json', dest='type')
p.add_argument('--html', action='store_const', const='html', dest='type')
p.add_argument('--bibtex', action='store_const', const='bibtex', dest='type')
--json
, --bibtex
, and --html
have the same effect as --type json
, --type bibtex
, and --type html
, respectively. As before, --type
can only take those three values as its argument. If more than one of these, the last one used takes effect.
Upvotes: 5
Reputation: 50076
argparse
already ships with everything you need to actually have the API you want, not the one you think you need.
choices
, not three separate flags.inputfilename
should be required.outputfilename
should be optional.import argparse
parser = argparse.ArgumentParser(description='Convert file to new type')
parser.add_argument('format', choices=["json", "bibtex", "html"])
parser.add_argument('inputfilename', type=str)
parser.add_argument('outputfilename', type=str, nargs="?")
args = parser.parse_args()
print(args)
This automatically provides the desired usage, without misusing optionals but gaining error handling instead.
$ ./converter json in.txt
Namespace(format='json', inputfilename='in.txt', outputfilename=None)
$ ./converter html in.txt out.html
Namespace(format='html', inputfilename='in.txt', outputfilename='out.html')
$ ./converter yaml in.txt
usage: converter [-h] {json,bibtex,html} inputfilename [outputfilename]
converter: error: argument format: invalid choice: 'yaml' (choose from 'json', 'bibtex', 'html')
Upvotes: 1
Reputation: 2159
Your issue is that argparse is expecting an input, especially since you have type=str
Try replacing the json arg with this instead
parser.add_argument('--json', action="store_true", help='Converts to json format')
Upvotes: 0
Reputation: 175
So the problem seems as if every argparse.arg requires an argument to be specified, be it anything. So you could set it to a boolean maybe. But I think maybe this approach could be better:
import sys
import argparse
parser = argparse.ArgumentParser(description='Convert file to new type')
parser.add_argument('--type', type=str, help='Converts to any of the following formats: 1 for json, 2 for bibtex and 3 for html')
parser.add_argument('--inputfilename', type=str, help='enter the original filename')
args = parser.parse_args()
filename=args.inputfilename
if args.type:
conversion_type = int(args.type)
if conversion_type == int(1):
print('Converting to json ...')
#conversion code here
elif conversion_type == 2:
print('Converting to bibtex ...')
#conversion code here
elif conversion_type == 3:
print('Converting to html ...')
#conversion code here
else:
print('No conversion type indicated')
Just add an arg for specifying the type. Based on that, convert or dont as per the conditionals.
Upvotes: -2