19

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?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Inactivist
  • 9,997
  • 6
  • 29
  • 41
  • 2
    Just in case anyone needs this, if --test took argument you could do: `python example.py --test=-1,2,3,4` – lababidi Oct 28 '14 at 21:16

3 Answers3

32

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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
9

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.

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • I saw the documentation, but was wondering if there were any tricks to alter the standard behavior. At this point, I think it would be better to require four separate positional arguments or use space delimiters than to use the `--` escape mechanism. Thanks for the help. – Inactivist Feb 04 '13 at 20:44
  • @Inactivist I updated my answer with a third method to allow `-1,2,3,4` style positional. – Bakuriu Feb 04 '13 at 22:18
0

I found this from another post somewhere and thought I would update this for the next time I search for how to solve this.

$ python example.py --test=-1,2,3,4

Should in theory work, it works for me with passing a single entry like

$ python example.py --test=-1