yang <fhsxf...@126.com> added the comment:

I ran into the same issue and looked into the code, and found it more 
complicated than I thought. The more I went on, more issues occur. I wonder if 
I should open a new issue, but I will first comment here. If you feel like this 
should be a new issue, I will open one then. And I apologize in advance for 
possible vaguenesses in this comment because I modified it several times as I 
explored the code and found more issues. (also because of my poor English):)

It seems the issue happens only on positional arguments but not optional ones. 
Empty optional arguments will not call `take_action` and default values are 
handled and converted after consuming all arguments.

It also leads to inconsistancy between positional and optional arguments 
behaviour. Positional arguments always go through `take_action`, but optional 
arguments don't if an argument doesn't appear.

This inconsistancy causes another I think is strange behaviour,

    parser = ArgumentParser()
    parser.add_argument('i', action='count')
    parser.parse_args([])

got

    Namespace(i=1)

On the other hand, in `_get_values` function, `_check_value` is called to 
handle `choices=`, but there is no such guard for optional arguments, which 
means,

    parser = ArgumentParser()
    parser.add_argument('-i', nargs='?', type=int, default='2', choices=[1])
    parser.parse_args([])

doesn't raise an error.

Besides Paul's two instructive solutions, I think it better to make both sides 
behave the same. However, I found things seem not that simple.

First, ZERO_OR_MORE, no default value, positional arguments have 
`required=True` by default, but

    parser.add_argument('foo', nargs='*')
    parser.parse_args([])

got no problems. So it at least appears not required. (The document says 
`required` is only for optionals, so I guess it's just a implementation level 
but not a user level thing)

Second, the last case above gives

    Namespace(foo=[])

which seems logically incorrect or at least controversial, because the default 
is not set and you give no arguments, how does this list come? The document 
says nothing about the case (it's true it's a very corner one) and it also 
differs from the optional arguments case which gives

    Namespace(foo=None)

A walk around which doesn't change it is possible and I've written a patch 
fixing it.
And I'm not sure what we usually do if I propose to make them give the same 
result, is a PEP needed or I just raise a discussion about it? The change might 
break current code.

----------
nosy: +fhsxfhsx

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue36078>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to