I'm somewhat new to using argparse and have run into a problem stemming from using an optional argument (list of files arbitrarily long) along with a positional argument (a single file). Here is a simple program that demonstrates this problem:
parser = argparse.ArgumentParser()
parser.add_argument("pos_file", help="Input file")
parser.add_argument("-v", "--verbose", action="store_true")
parser.add_argument("-l", "--optional_list", help="file list", nargs="+")
args = parser.parse_args()
When I call this program using the command:
python test_arg_parse.py -l file1.txt file2.txt pos_file.txt
Argparse fails, mentioning that there are too few arguments. I understand why this is happening - argparse assumes that pos_file.txt is part of the optional list and therefore doesn't find a positional argument - but I don't understand why argparse doesn't always consider the last argument to be positional. This can be solved by placing the verbose argument between the optional list and the positional argument:
python test_arg_parse.py -l file1.txt file2.txt -v pos_file.txt
Which argparse happily processes. However, I don't want to enforce this strict ordering of arguments and I would like to keep the list optional and the single file positional. I have looked into using the append
functionality, but it doesn't seem reasonable considering the list can be arbitrarily long.
Is there a nice way of accomplishing this?
Parsing is driven by the order of values in the command line, having first identified which strings look like flags (optionals) and which are just arguments.
python test_arg_parse.py -l file1.txt file2.txt pos_file.txt
It first checks the start of the list for positional arguments - none. Then for flags - it finds '-l'. That takes '+', so it's given everything up to the next flag (or the end). Now there's nothing left on the list for 'pos_file'.
There is a bug/issue about this, and I proposed a patch that would check the positionals, and hold back on giving the optional everything. But that requires too much fiddling with the core parsing code, and hasn't gotten very far in approval process.
So for now, using a positional like this after an open ended argument (optionals or positionals) doesn't work
python test_arg_parse.py pos_file.txt -l file1.txt file2.txt
should work. But if you want more control, define pos_file
as a flagged argument, not a positional.