Issue 28734: argparse: successive parsing wipes out nargs=? values (original) (raw)

Issue28734

Created on 2016-11-18 15:41 by ajstewart, last changed 2022-04-11 14:58 by admin.

Messages (4)
msg281128 - (view) Author: Adam Stewart (ajstewart) * Date: 2016-11-18 15:41
I'm writing a wrapper that optionally accepts a file and reads more options from that file. The wrapper then needs to pass all of these options and the file to another program (qsub). Here is a minimal example to reproduce the behavior I'm seeing: >>> import argparse >>> parser = argparse.ArgumentParser() >>> parser.add_argument('-a') >>> parser.add_argument('file', nargs='?') >>> args = parser.parse_args(['-a', '3', 'myFile']) >>> print(args) Namespace(file='myFile', a='3') >>> parser.parse_args(['-a', '4'], namespace=args) >>> print(args) Namespace(file=None, a='4') The behavior I expect is that the file should remain as 'myFile', but it is being wiped out. Is there any way to prevent this, or is this actually a bug? I can recreate this problem in Python 2.7 and 3.5.
msg281137 - (view) Author: Wolfgang Maier (wolma) * Date: 2016-11-18 16:16
try this: parser.add_argument('file', nargs='?', default = argparse.SUPPRESS) I don't think this is a bug. The default for the default parameter is None so the behavior is consistent with the documentation.
msg281138 - (view) Author: Adam Stewart (ajstewart) * Date: 2016-11-18 16:19
Works for me, thanks Wolfgang!
msg299354 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2017-07-27 23:13
The problem described here is restricted to `?` and `*' positionals, and is caused by the subtle way in which 'empty' optional positionals are handled. The regular handling of defaults at the start of `parse_known_args` works fine. The default is only written to the namespace if something isn't there already. if not hasattr(namespace, action.dest): if action.default is not SUPPRESS: setattr(namespace, action.dest, action.default) But a positional with ? or * is always 'seen' because an empty list of strings satisfies the nargs pattern. 'get_values()' has special handling for this case: if not arg_strings and action.nargs == OPTIONAL: .... value = action.default That is, it replaces the empty list with the 'default'. But take_action(), which does the actual saving, is conditional: # take the action if we didn't receive a SUPPRESS value # (e.g. from a default) if argument_values is not SUPPRESS: action(self, namespace, argument_values, option_string) That explains why 'default=SUPPRESS' solves this issue. It's enough to satisfy the OP's situation, but I don't think it's a robust fix. I don't have a patch idea yet, but this probably should be reopened so there's a record of the potential problem. More on the complications raised by these 'seen default actions' in http://bugs.python.org/issue18943
History
Date User Action Args
2022-04-11 14:58:39 admin set github: 72920
2017-07-29 00:02:53 terry.reedy set nosy: + bethard
2017-07-27 23:13:28 paul.j3 set status: closed -> opennosy: + paul.j3messages: +
2016-11-18 16:19:02 ajstewart set status: open -> closedresolution: not a bugmessages: +
2016-11-18 16:16:36 wolma set nosy: + wolmamessages: +
2016-11-18 15:41:15 ajstewart create