Positional argument after optional argument list

Question:

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?

Asked By: Tex4066

||

Answers:

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.

Answered By: hpaulj

As users noted, once you pass in a flag (optional) argument argparse will not know how to handle positional arguments that come afterwards. One solution I found is to use the pseudo-argument -- which tells argparse that the next parameters are positional. From the python argparse docs

If you have positional arguments that must begin with – and don’t look
like negative numbers, you can insert the pseudo-argument ‘–‘ which
tells parse_args() that everything after that is a positional argument

You can use the updated command:

python test_arg_parse.py -l file1.txt file2.txt -- pos_file.txt
Answered By: jjbskir
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.