paul j3 <ajipa...@gmail.com> added the comment:

At the start of parse_known_args, all defaults (except SUPPRESS ones) are 
placed in the namespace:

        # add any action defaults that aren't present
        for action in self._actions:
            if action.dest is not SUPPRESS:
                if not hasattr(namespace, action.dest):
                    if action.default is not SUPPRESS:
                        setattr(namespace, action.dest, action.default)

at the end of _parse_known_args there's a conditional expression that cleans up 
remaining defaults that are strings, by passing them through the 'type` 
callable:

    setattr(namespace, action.dest,
        self._get_value(action, action.default))

Read the comments to see why this default setting is done in two parts.

The pattern of defaults in msg342122 with optionals is consistent with that.  
If the argument is not provided, the default appears.  

In the first example of that message, the REMAINDER is given all the remaining 
strings including the '--bar', so there is nothing left to trigger the '--bar' 
optional argument, and it retains the default.

The difference for positionals is due to how the '*' and '...' are handled in 
_getvalues.  Both may be filled with an empty list of values.

        # when nargs='*' on a positional, if there were no command-line
        # args, use the default if it is anything other than None
        elif (not arg_strings and action.nargs == ZERO_OR_MORE and
              not action.option_strings):
            if action.default is not None:
                value = action.default
            else:
                value = arg_strings
            self._check_value(action, value)

        ....

        # REMAINDER arguments convert all values, checking none
        elif action.nargs == REMAINDER:
            value = [self._get_value(action, v) for v in arg_strings]

In the case of '*', the default is, effectively, placed back on the namespace.  
REMAINDER does not - the empty list is put in the namespace.

In _get_positional_kwargs,

        # mark positional arguments as required if at least one is
        # always required
        if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]:
            kwargs['required'] = True
        if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs:
            kwargs['required'] = True

That last conditional is a little puzzling, but I suspect it has to do with 
mutually_exclusive_groups,  A '*' positional can be a member of a group if it 
has a default.

Anyways, we could add a test at this point like:

        if kwargs.get('nargs') == REMAINDER and 'default' in kwargs:
            msg = _("'default' is not allowed with a REMAINDER positional")
            raise TypeError(msg)

But reviewing the code I notice another difference.  'choices' are not honored 
for REMAINDER.  That makes a lot of sense.  REMAINDER is supposed to be a catch 
all, documented as something that might be passed on to another parser.  This 
parser shouldn't be doing anything with those values.

The documentation reads:

argparse.REMAINDER. All the remaining command-line arguments are gathered into 
a list. This is commonly useful for command line utilities that dispatch to 
other command line utilities:

I think REMAINDER has another quirk.  It doesn't work as the first (and only?) 
argument.   There should be a bug/issue to that effect.

https://bugs.python.org/issue17050, argparse.REMAINDER doesn't work as first 
argument

In https://bugs.python.org/issue17050#msg315716, I suggest removing REMAINDER 
from the docs.  We can leave the code as is, in case anyone is using still 
using it.  But it is probably too much work to make the code and docs match, 
both for that issue, and for this.


'*' plus '--' gives almost the same behavior.  So does parse_known_args.

----------

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

Reply via email to