3

Here is my argparse sample say sample.py

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-p", nargs="+", help="Stuff")
args = parser.parse_args()
print args

Python - 2.7.3

I expect that the user supplies a list of arguments separated by spaces after the -p option. For example, if you run

$ sample.py -p x y 
Namespace(p=['x', 'y'])

But my problem is that when you run

$ sample.py -p x -p y
Namespace(p=['y'])

Which is neither here nor there. I would like one of the following

  • Throw an exception to the user asking him to not use -p twice instead just supply them as one argument
  • Just assume it is the same option and produce a list of ['x','y'].

I can see that python 2.7 is doing neither of them which confuses me. Can I get python to do one of the two behaviours documented above?

1
  • You certainly can get it to do what you want by subclassing argparse.Action. Commented Oct 7, 2013 at 15:39

2 Answers 2

3

Note: python 3.8 adds an action="extend" which will create the desired list of ['x','y']

To produce a list of ['x','y'] use action='append'. Actually it gives

Namespace(p=[['x'], ['y']])

For each -p it gives a list ['x'] as dictated by nargs='+', but append means, add that value to what the Namespace already has. The default action just sets the value, e.g. NS['p']=['x']. I'd suggest reviewing the action paragraph in the docs.

optionals allow repeated use by design. It enables actions like append and count. Usually users don't expect to use them repeatedly, or are happy with the last value. positionals (without the -flag) cannot be repeated (except as allowed by nargs).

How to add optional or once arguments? has some suggestions on how to create a 'no repeats' argument. One is to create a custom action class.

Sign up to request clarification or add additional context in comments.

4 Comments

It is a pity that this is not available by default... sigh
Do you have suggestions on how to specify 'only allow one use of this option'? How would it be marked on the usage line?
I would have thought that is the default i.e, being able to specify an option only once. I see your point though, all linux commands are able to take the same option multiple times so am not too sure what to compare it with.
But there is an inherent problem if the developer forgot to do nargs=+ the argparse simply assumes the latest definition of a variable. There probably isn't an easy answer to this. I will try some linux commands on how they handle same option being specified multiple times.
0

I ran into the same issue. I decided to go with the custom action route as suggested by mgilson.

import argparse
class ExtendAction(argparse.Action):
  def __call__(self, parser, namespace, values, option_string=None):
    if getattr(namespace, self.dest, None) is None:
      setattr(namespace, self.dest, [])
    getattr(namespace, self.dest).extend(values)
parser = argparse.ArgumentParser()
parser.add_argument("-p", nargs="+", help="Stuff", action=ExtendAction)
args = parser.parse_args()
print args

This results in

$ ./sample.py -p x -p y -p z w
Namespace(p=['x', 'y', 'z', 'w'])

Still, it would have been much neater if there was an action='extend' option in the library by default.

1 Comment

If you are subclassing your ArgumentParser, it might be convenient to 'register' your custom class. See the _ActionsContainer class.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.