In my script I have 3 positional arguments and 1 optional argument. One of the three positional arguments is required and the rest is optional (as specified using narg='?'
).
My optional argument doesn't pass any other arguments (action ='store_true') and is just there to enable sorting which will be implemented at a later time.
However, my problem is that my optional argument only works when it is the first or last argument in the script call.
Below is my script so far:
parser = argparse.ArgumentParser()
parser.add_argument("-s", "--sort", help="option to sort the ip addresses", action='store_true')
parser.add_argument("file_name", help="Name of the file containing the tcpdump")
parser.add_argument("source_IP", nargs='?', type=str, help="Source ip to search")
parser.add_argument("dest_IP", nargs='?', type=str, help="Destination ip to search")
args = parser.parse_args()
If I enter my -s
between any of the other positional arguments I get an error.
Ex: ./my_script file.txt -s 192.168.152.32 192.168.0.25
usage: 1 [-h] [-s] file_name [source_IP] [dest_IP]
1: error: unrecognized arguments: 192.168.152.32 192.168.0.25
My goal is to be able to enter my optional argument (-s
) anywhere in the script call and have it working.
You have three positional arguments, but two of them are also optional thanks to
nargs='?'
.argparse
is getting screwed up because it sees the positional filename, and then has to choose arbitrarily between interpreting the-s
as the optional positionalsource
, or as the switch. Either interpretation is valid (it's not doing complicated backtracking parsing to try to find some legal interpretation of the arguments that would allow it to complete; doing so with some argument types could lead to very bad behavior, like opening a file, then backtracking, closing it, and opening something else).Short answer: In general, optional arguments should be either all positional, or all switches. Mixing and matching introduces complications that would make parsing a complicated recursive process that could only heuristically guess at the correct parsing (particularly with
nargs='*'
andnargs='+'
, but even'?'
causes problems as you see). Removing thenargs
qualifier fromsource
anddest
, or leaving them optional and converting to switches will allow-s
to be passed in whatever order you like.This problem is a subtile one, and requires a good understanding of how
argparse
parses postionals and optionals.If all the positionals took one argument (the default
nargs
), then the-s
could occur anywhere - start, end, or between any positional.What happens with:
is that
source_IP
,dest_IP
are both consumed (and set to their defaults) whenfile_name
is parsed. It then handles-s
. Now there are 2 strings left, but no positionals to consume them, hence theunrecognized arguments
error. Note that./my_script file.txt
runs fine, as does./my_script file.txt -s
. In all 3 cases, the 2 IP arguments are consumed at the same time asfile_name
.parse_args
alternates between consuming positionals and optionals. It will consume as many positionals at time as there strings (think of a greedy regex expression). Sincesource_IP
anddest_IP
are ok with 0 args, all three are consumed the first time it handles positionals.There isn't a neat fix for the user, except to be wary of using
nargs='?'
positionals.There is a bug/issue that tries to fix this. But the fix isn't trivial. The parser has to 'look ahead', noting that it could delay parsing these '?' arguments.
Your parser runs fine with the
argparse
patched as proposed in that issue. But there's quite a backlog of potential patches for argparse.