Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse positional arguments with leading minus sign (negative numbers) using argparse

I would like to parse a required, positional argument containing a comma-separated list of integers. If the first integer contains a leading minus ('-') sign, argparse complains:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional')
parser.add_argument('-t', '--test', action='store_true')
opts = parser.parse_args()
print opts

$ python example.py --test 1,2,3,4
Namespace(positional='1,2,3,4', test=True)

$ python example.py --test -1,2,3,4
usage: example.py [-h] [-t] positional
example.py: error: too few arguments

$ python example.py --test "-1,2,3,4"
usage: example.py [-h] [-t] positional
example.py: error: too few arguments

I've seen people suggest using some other character besides - as the flag character, but I'd rather not do that. Is there another way to configure argparse to allow both --test and -1,2,3,4 as valid arguments?

like image 675
Inactivist Avatar asked Feb 04 '13 19:02

Inactivist


2 Answers

You need to insert a -- into your command-line arguments:

$ python example.py --test -- -1,2,3,4
Namespace(positional='-1,2,3,4', test=True)

The double-dash stops argparse looking for any more optional switches; it's the defacto standard way of handling exactly this use case for command-line tools.

like image 84
Martijn Pieters Avatar answered Oct 11 '22 03:10

Martijn Pieters


From the documentation:

The parse_args() method attempts to give errors whenever the user has clearly made a mistake, but some situations are inherently ambiguous. For example, the command-line argument -1 could either be an attempt to specify an option or an attempt to provide a positional argument. The parse_args() method is cautious here: positional arguments may only begin with - if they look like negative numbers and there are no options in the parser that look like negative numbers:

Since -1,2,3,4 does not look like a negative number you must "escape" it with the -- as in most *nix systems.

An other solution would be to use nargs for the positional and pass the numbers as space separated:

#test.py
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('positional', nargs='*') #'+' for one or more numbers

print parser.parse_args()

Output:

$ python test.py -1 2 3 -4 5 6
Namespace(positional=['-1', '2', '3', '-4', '5', '6'])

A third way to obtain what you want is to use parse_known_args instead of parse_args. You do not add the positional argument to the parser and parse it manually instead:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--test', action='store_true')

parsed, args = parser.parse_known_args()
print parsed
print args

Result:

$ python test.py  --test -1,2,3,4                                            
Namespace(test=True)
['-1,2,3,4']    

This has the disadvantage that the help text will be less informative.

like image 32
Bakuriu Avatar answered Oct 11 '22 02:10

Bakuriu