Skip to content
This repository has been archived by the owner on May 13, 2021. It is now read-only.

optionals with nargs='+' can't be followed by positionals #20

Closed
GoogleCodeExporter opened this issue Sep 12, 2015 · 6 comments
Closed

Comments

@GoogleCodeExporter
Copy link

What steps will reproduce the problem?

>>> import argparse
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('--badger', nargs='+')
>>> parser.add_argument('spam')
>>> parser.parse_args('--badger A B C D'.split())

What is the expected output?

>>> parser.parse_args('--badger A B C D'.split())
Namespace(badger=['A', 'B', 'C'], spam='D')

What do you see instead?

>>> parser.parse_args('--badger A B C D'.split())
usage: PROG [-h] [--badger BADGER [BADGER ...]] spam
PROG: error: too few arguments

Please use labels and text to provide additional information.

Basically, the nargs='+' causes the optional to consume all the arguments
following it, even though we should know that we need to save one for the
final positional argument.

A workaround is to specify '--', e.g.:

>>> parser.parse_args('--badger A B C -- D'.split())
Namespace(badger=['A', 'B', 'C'], spam='D')

Original issue reported on code.google.com by steven.b...@gmail.com on 26 Jul 2009 at 4:28

@GoogleCodeExporter
Copy link
Author

It is not a defect. For example, I'd expect the following logic:

nargs='+' -> greedy (consumes all it can (until the next optional argument))
nargs='+?' -> non-greedy (if there are unpopulated positionals it leaves some
arguments to them in order that the whole sequence of arguments could be 
consumed)

Original comment by 4kir4...@gmail.com on 10 Sep 2009 at 3:26

@GoogleCodeExporter
Copy link
Author

I disagree. Consider the regular expression '(OA+)?(A)':

  >>> re.findall(r'(OA+)?(A)', 'OAAA')
  [('OAA', 'A')]
  >>> re.findall(r'(OA+)?(A)', 'OA')
  [('', 'A')]

That is, even though the + is greedy, it isn't so greedy as to force a match to 
fail
when it could succeed.

Right now, argparse does do this kind of regular-expression style matching for
positional arguments, but it does that separately from what it does for optional
arguments. That's where the problem arises from.

I imagine the solution is to build a regular expression of the possible things a
parser could match. So given a parser like::

  parser = argparse.ArgumentParser()
  parser.add_argument('-w')
  parser.add_argument('-x', nargs='+')
  parser.add_argument('y')
  parser.add_argument('z', nargs='*')

the regular expression might look something like (where positionals have been
replaced by the character A)::

  (-w A)? (-x A+)? A (-w A)? (-x A+)? A* (-w A)? (-x A+)?

Note that the optionals can appear between any positionals, so I have to repeat 
their
regular expressions multiple times. Because of this, I worry about how big the
regular expression might grow to be for large parsers. But maybe it's the right 
solution.

Original comment by steven.b...@gmail.com on 12 Sep 2009 at 5:24

@GoogleCodeExporter
Copy link
Author

You're right for *optional* arguments (implicit '()?'), but for *required* 
arguments
I'd expect a greedy behavior for '+' sign (as it behaves in regexs). 

Original comment by 4kir4...@gmail.com on 16 Sep 2009 at 10:07

@GoogleCodeExporter
Copy link
Author

For required positional arguments, I believe this should already be the case. I
actually create the regular expression for them and try to match them. For 
example:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('foo', nargs='+')
>>> parser.add_argument('bar', nargs='+')
>>> parser.parse_args('ABCD')
Namespace(bar=['D'], foo=['A', 'B', 'C'])

Note that "foo" gets most of the items because it was first.

Original comment by steven.b...@gmail.com on 17 Sep 2009 at 12:22

@GoogleCodeExporter
Copy link
Author

Issue 42 has been merged into this issue.

Original comment by steven.b...@gmail.com on 2 Nov 2009 at 4:41

@GoogleCodeExporter
Copy link
Author

Moved to http://bugs.python.org/issue9338

Original comment by steven.b...@gmail.com on 23 Jul 2010 at 10:48

  • Changed state: Duplicate

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant