msg114108 - (view) |
Author: Martin Pengelly-Phillips (thesociable) |
Date: 2010-08-17 09:19 |
Variable argument count plays badly with choices.
Example:
========
>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('choices', nargs='*', default='a', choices=['a',
'b', 'c'])
>>> args = parser.parse_args()
>>> print type(args.choices)
<type 'str'>
>>> args = parser.parse_args(['a'])
>>> print type(args.choices)
<type 'list'>
If the user specifies the value on the command line then a list is used, but if the value comes from the default a string is used.
Unfortunately, changing default to a list value gives an error:
error: argument choices: invalid choice: ['a'] (choose from 'a', 'b',
'c')
Additionally, this means it is also not possible to use default=['a', 'c'].
The current workaround is to create a custom type:
def my_type(string):
if string not in ['a', 'b', 'c']:
raise TypeError
return string
|
msg151780 - (view) |
Author: Michał M. (regis) |
Date: 2012-01-22 17:55 |
Maybe it will sound strange, but what is this task REALLY about? I mean - I can see two problems here, but no clear information about which problem is a real problem and - if it is - what is the expected behavior.
Problems I can see are:
1) Type of returned value changes depending on the value source (list for user provided or list for default)
2) It's impossible to set list as 'default'
I understand that this task concentrates on 1st one, however changing behavior described in 2nd would - in my oppinion - fix 1st one too - am I right? User would "define" the returned type by defining the 'default' as a list or string.
But - if we assume that we only discuss 1st problem here - what is the expected behavior? Provided workaround is suggesting that we expect string, but - basing on current nargs behavior and "intuition" - I'd rather expect to get a list, when I define nargs with "*" or "+". This sounds "natural" for me.
Could someone explain me the problem and the expected behavior in a clear way?
Sorry if I'm not clear, but it's my first post here and maybe I've missed something in the description or I assume something that is not true (especially that the task is quite old... ;) ).
|
msg151807 - (view) |
Author: Michał M. (regis) |
Date: 2012-01-23 10:50 |
Of course I've made a mistake:
"list for user provided or list for default"
should be:
"list for user provided or STRING for default"
|
msg151857 - (view) |
Author: Martin Pengelly-Phillips (thesociable) |
Date: 2012-01-23 22:23 |
The real issue is that the choices flag does not work with a default flag and * nargs.
The following works as expected:
>>> parser.add_argument('chosen', nargs='*', default=['a'])
>>> print(parser.parse_args())
Namespace(chosen=['a'])
>>> print(parser.parse_args(['a', 'b']))
Namespace(chosen=['a', 'b'])
Introducing a choices constraint breaks down when using the defaults:
>>> parser.add_argument('chosen', nargs='*', default=['a'], choices=['a', 'b'])
>>> print(parser.parse_args(['a']))
Namespace(chosen=['a'])
>>> print(parser.parse_args())
error: argument chosen: invalid choice: ['a'] (choose from 'a', 'b')
I would expect instead to have Namespace.chosen populated with the default list as before, but the choices constraint check does not validate correctly.
I think that changing the choices constraint logic to iterate over the default values if nargs results in a list would be a possible solution.
|
msg166086 - (view) |
Author: Steven Bethard (bethard) *  |
Date: 2012-07-21 22:19 |
I agree that this looks like a bug. I think the fix is something like the attached patch, but it needs some tests to make sure that it fixes your problem.
|
msg174642 - (view) |
Author: Cédric Krier (ced) * |
Date: 2012-11-03 16:00 |
Here is a new version of the patch with tests
|
msg191224 - (view) |
Author: Cédric Krier (ced) * |
Date: 2013-06-15 19:02 |
ping
|
msg191932 - (view) |
Author: paul j3 (paul.j3) *  |
Date: 2013-06-27 06:33 |
I've added 2 more tests,
one with default='c', which worked before.
one with default=['a','b'], which only works with this change.
http://bugs.python.org/issue16878 is useful reference, since it documents
the differences between nargs="?" and nargs="*", and their handling of
their defaults.
|
msg192258 - (view) |
Author: paul j3 (paul.j3) *  |
Date: 2013-07-03 21:36 |
Change "choices='abc'" to "choices=['a', 'b', 'c']", as discussed in issue 16977 (use of string choices is a bad example)
|
msg192716 - (view) |
Author: paul j3 (paul.j3) *  |
Date: 2013-07-09 03:07 |
The patch I just posted to http://bugs.python.org/issue16468 uses this fix.
|
msg217677 - (view) |
Author: paul j3 (paul.j3) *  |
Date: 2014-05-01 04:46 |
There's a complicating issue - should these default values be passed through the type function?
In most cases in `_get_values`, the string first goes through `_get_value`, and then to `_check_value`.
For example the 'else:' case:
value = [self._get_value(action, v) for v in arg_strings]
for v in value:
self._check_value(action, v)
The '*' positional case could coded the same way, allowing:
parser.add_argument('foo',
nargs='*',
type=int,
choices=range(5),
default=['0',1,'2'])
and objecting to
default=[6,'7','a'] # out of range string or int or invalid value
This does impose a further constraint on the 'type' function, that it accepts a converted value. e.g. int(1) is as valid as int('1').
But we need to be careful that this case is handled in a way that is consistent with other defaults (including the recent change that delayed evaluating defaults till the end).
|
msg285865 - (view) |
Author: paul j3 (paul.j3) *  |
Date: 2017-01-20 01:28 |
Recent StackOverFlow question related to this issue
http://stackoverflow.com/questions/41750896/python-argparse-type-inconsistencies-when-combining-choices-nargs-and-def/41751730#41751730
|
msg347742 - (view) |
Author: João Eiras (João Eiras) |
Date: 2019-07-12 12:55 |
Another workaround for who might ever need it. The benefit of this solution comparing to a custom type is that argparse will generate the help string properly with the choices, and this solution does workaround the place when the bug happens:
class Choices(tuple):
# Python bug https://bugs.python.org/issue27227
def __new__(cls, *args, **kwargs):
x = tuple.__new__(cls, *args, **kwargs)
Choices.__init__(x, *args, **kwargs)
return x
def __init__(self, *args, **kwargs):
self.default = []
def __contains__(self, item):
return tuple.__contains__(self, item) or item is self.default
choices = Choices(("value1", "value2", "value3", ...))
parser.add_argument(
...,
nargs="*",
choices=choices,
default=choices.default,
...)
|
msg356859 - (view) |
Author: Mihail Milushev (lanzz) |
Date: 2019-11-18 11:14 |
It looks like choices are broken for nargs='*' even without using default:
>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('test', nargs='*', choices=['foo', 'bar', 'baz'])
_StoreAction(option_strings=[], dest='test', nargs='*', const=None, default=None, type=None, choices=['foo', 'bar', 'baz'], help=None, metavar=None)
>>> parser.parse_args([])
usage: [-h] [{foo,bar,baz} [{foo,bar,baz} ...]]
: error: argument test: invalid choice: [] (choose from 'foo', 'bar', 'baz')
|
msg357064 - (view) |
Author: Jan Hutař (Jan Hutař) |
Date: 2019-11-20 11:55 |
I think there is a same issue with "nargs='+'" - if you are aware of the, please ignore me.
$ python3 --version
Python 3.7.5
With "choices=...", there seems to be a problem:
$ cat bbb.py
#!/usr/bin/env python3
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('choices', nargs='*',
default=['a', 'b', 'c'],
choices=['a', 'b', 'c'])
args = parser.parse_args()
print(args)
$ ./bbb.py
usage: bbb.py [-h] [{a,b,c} [{a,b,c} ...]]
bbb.py: error: argument choices: invalid choice: ['a', 'b', 'c'] (choose from 'a', 'b', 'c')
$ ./bbb.py a c
Namespace(choices=['a', 'c'])
but without choices it works:
$ cat bbb.py
#!/usr/bin/env python3
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('choices', nargs='*',
default=['a', 'b', 'c'])
args = parser.parse_args()
print(args)
$ ./bbb.py
Namespace(choices=['a', 'b', 'c'])
$ ./bbb.py a c
Namespace(choices=['a', 'c'])
As I said, if this is a known issue, I'm sorry for the noise.
|
msg374581 - (view) |
Author: James Corbett (jameshcorbett) * |
Date: 2020-07-29 18:22 |
I would love to get this issue resolved; it seems like everyone agrees that it's a bug. It came up for me recently: https://bugs.python.org/issue41047. Judging from the comments above, the consensus is that the relevant line, `self._check_value(action, value)` should either be replaced with something like `if isinstance(value, collections.abc.Sequence): for v in value: self._check_value(action, v)` or be removed entirely.
I think the line should just be removed. I think it's fair to assume that users of `argparse` know what they're doing, so I think they should be allowed to pass default values that conflict with `choices`. Also, removing the line makes the behavior consistent with the optionals, which don't check whether default values are in `choices`. See the below script:
```
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--foo", nargs="+", default=[-1], choices=range(10))
parser.add_argument("--bar", nargs="*", default=-1, choices=range(10))
parser.add_argument("pos", nargs="?", default=-1, choices=range(10))
args = parser.parse_args()
print(args)
if __name__ == '__main__':
main()
```
Which yields:
```
$ python argparse_test.py
Namespace(foo=[-1], bar=-1, pos=-1)
```
|
msg374592 - (view) |
Author: Raymond Hettinger (rhettinger) *  |
Date: 2020-07-29 23:13 |
Paul, what is your current thinking on this?
|
|
Date |
User |
Action |
Args |
2022-04-11 14:57:05 | admin | set | github: 53834 |
2020-12-21 23:36:46 | rhettinger | set | assignee: rhettinger -> |
2020-12-01 04:52:56 | zumoshi | set | nosy:
+ zumoshi
|
2020-07-29 23:13:20 | rhettinger | set | messages:
+ msg374592 |
2020-07-29 18:22:56 | jameshcorbett | set | nosy:
+ jameshcorbett messages:
+ msg374581
|
2019-11-20 11:55:54 | Jan Hutař | set | nosy:
+ Jan Hutař messages:
+ msg357064
|
2019-11-18 11:14:30 | lanzz | set | nosy:
+ lanzz messages:
+ msg356859
|
2019-08-30 06:26:19 | rhettinger | set | priority: high -> normal versions:
+ Python 3.9, - Python 2.7, Python 3.5, Python 3.6 |
2019-08-30 03:30:47 | rhettinger | set | assignee: bethard -> rhettinger
nosy:
+ rhettinger |
2019-07-12 13:00:14 | João Eiras | set | nosy:
- João Eiras
|
2019-07-12 12:55:53 | João Eiras | set | nosy:
+ João Eiras messages:
+ msg347742
|
2018-10-16 17:01:43 | sebix | set | nosy:
+ sebix
|
2018-04-26 12:05:40 | macfreek | set | nosy:
+ macfreek
|
2017-01-20 01:28:04 | paul.j3 | set | messages:
+ msg285865 |
2017-01-19 23:02:30 | paul.j3 | set | priority: normal -> high |
2016-06-12 14:30:54 | berker.peksag | set | nosy:
+ berker.peksag stage: needs patch -> patch review
versions:
+ Python 3.5, Python 3.6, - Python 3.2 |
2016-06-12 14:30:29 | berker.peksag | link | issue27227 superseder |
2015-08-29 10:19:14 | deleted250130 | set | nosy:
+ deleted250130
|
2014-05-02 02:04:07 | paul.j3 | set | files:
+ notes.txt |
2014-05-01 04:46:43 | paul.j3 | set | messages:
+ msg217677 |
2013-07-09 03:07:58 | paul.j3 | set | messages:
+ msg192716 |
2013-07-03 21:36:35 | paul.j3 | set | files:
+ issue9625_2.patch
messages:
+ msg192258 |
2013-06-27 06:33:20 | paul.j3 | set | files:
+ issue9625_1.patch
messages:
+ msg191932 |
2013-06-26 20:36:39 | paul.j3 | set | nosy:
+ paul.j3
|
2013-06-15 19:02:34 | ced | set | messages:
+ msg191224 |
2012-11-03 16:00:08 | ced | set | files:
+ issue9625.patch nosy:
+ ced messages:
+ msg174642
|
2012-07-21 22:19:22 | bethard | set | files:
+ issue9625.diff keywords:
+ patch messages:
+ msg166086
|
2012-01-23 22:23:14 | thesociable | set | messages:
+ msg151857 title: argparse: Problem with defaults for variable nargs -> argparse: Problem with defaults for variable nargs when using choices |
2012-01-23 10:50:51 | regis | set | messages:
+ msg151807 |
2012-01-22 17:55:46 | regis | set | nosy:
+ regis messages:
+ msg151780
|
2010-11-02 22:01:00 | eric.araujo | set | nosy:
+ eric.araujo
|
2010-08-21 22:39:06 | georg.brandl | set | assignee: bethard |
2010-08-17 19:08:29 | eric.smith | set | nosy:
+ eric.smith
|
2010-08-17 13:22:56 | bethard | set | nosy:
+ bethard stage: needs patch
versions:
+ Python 2.7, Python 3.2, - Python 2.6 |
2010-08-17 09:19:03 | thesociable | create | |