-
-
Notifications
You must be signed in to change notification settings - Fork 30.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
argparse does not preserve namespace with subparser defaults #89398
Comments
The following snippet demonstrates the problem. If a subparser flag has a default set, argparse will override the existing value in the provided 'namespace' if the flag does not appear (e.g., if the default is used): import argparse
parser = argparse.ArgumentParser()
sub = parser.add_subparsers()
example_subparser = sub.add_parser("example")
example_subparser.add_argument("--flag", default=10)
print(parser.parse_args(["example"], argparse.Namespace(flag=20))) This should return 'Namespace(flag=20)' because 'flag' already exists in the namespace, but instead it returns 'Namespace(flag=10)'. This intended behavior is described and demonstrated in the second example here: https://docs.python.org/3/library/argparse.html#default Lib's behavior is correct for the non-subparser cause. |
I haven't studied or tested this change, but it looks like a partial retraction of https://bugs.python.org/issue9351 Originally the main namespace was passed to the subparser. Steven Bethard changed it so that the subparser got a fresh namespace, and all values were copied to the main namespace. I and others raised the question of how it affected user provided values https://bugs.python.org/issue27859 Looks like this patch tries to solve some problems by moving the self._defaults step to the end of parser_know_args. I view that change with some trepidation. Handling defaults is tricky enough, with setting them at the start, but then only passing them through 'type' at the end if they still match the original strings. Mostly I've been telling StackOverflow questioners that it best not to use the same argument 'dest' in both the main and subparsers. Flags can be the same, but the 'dest' should be different to avoid conflicts over which default has priority. Again, I haven't tested this change, but I have a gut feeling it could have backward compatibility issues. |
I just downloaded this This change makes it impossible to use a subparser argument if it is defined in the user provided namespace, or by the main parser. It blocks not only subparser default, but also user input. It has reverted the 9351 patch which dates to 2014. |
parser = argparse.ArgumentParser()
sub = parser.add_subparsers()
example_subparser = sub.add_parser("example")
example_subparser.add_argument("--flag", default=10)
print(parser.parse_args(["example","--flag=15"], argparse.Namespace(flag=20))) still returns flag=20 User input should override values set by the provided Namespace. |
I should study previous posts in more detail, but here are some thoughts on correctly handling user namespace. At the start of if namespace is None:
namespace = Namespace() We need to hang on to a copy of this namespace, e.g. call it import copy
orig_namespace = copy.copy(namespace) In _SubParsersAction.__call__, pass this copy to the subparser (instead of None):
Prior to 9351, the main namespace was passed to the subparser
The trick is to get orig_namespace from the main parse_known_args to SubParsersAction.__call__ method. in a 9351 post I explore the idea of allowing the user to specify a 'sub_namespace' for the subparser. https://bugs.python.org/msg230056 In any case, this is a complicated issue that needs careful thought and more extensive testing. I didn't entirely like the original 9351 change, but since that's been part of argparse for many years, we need to very careful about clobbering it. |
Can confirm that this patch DOES cause backward compatibility issues, as paul.j3's gut feeling said it could. One of my projects, testing against 3.6-3.9, now fails its test suite on Python 3.9.8, which includes this change. Arguments passed to a subparser are indeed ignored in lieu of default values. We are tracking the problem in our own issue, sopel-irc/sopel#2210 I, for one, am not amused that 7-year-old behavior was "clobbered" (as previously described in this thread) in a patch release. |
I can also confirm that there is an regression in Python 3.9.8 regarding argparse For example, using watchdog 2.1.6 package Python 3.9.7 (correct behaviour) In [1]: from watchdog import watchmedo
In [2]: watchmedo.cli.parse_args(["auto-restart", "echo", "123"])
Out[2]: Namespace(command='echo', command_args=['123'], directories=None, patterns='*', ignore_patterns='', ignore_directories=False, recursive=False, timeout=1.0, signal='SIGINT', debug_force_polling=False, kill_after=10.0, func=<function auto_restart at 0x7f296d1d9dc0>) Python 3.9.8 (incorrect behaviour) In [1]: from watchdog import watchmedo
In [2]: watchmedo.cli.parse_args(["auto-restart", "echo", "123"])
Out[2]: Namespace(command='auto-restart', command_args=['123'], directories=None, patterns='*', ignore_patterns='', ignore_directories=False, recursive=False, timeout=1.0, signal='SIGINT', debug_force_polling=False, kill_after=10.0, func=<function auto_restart at 0x7fc39480cee0>) |
This has caused a regression, also reported here: Can we please get some attention from @rhettinger and @ambv? |
Unless anyone objects, I'll revert this across all affected branches. |
Go for it, Raymond. |
How long until the next bugfix releases for 3.9 and 3.10? To avoid further snowballing, it would be great to have this reversion pushed out soonish. |
The next bugfixe release of 3.10 is the 6th of December |
Only 3.9 needs an expedited rerelease. |
I've restored the prior state of affairs. Leaving this issue open because it still isn't clear what should be guaranteed or whether further improvements need to be made. |
Paul, should this be closed or do you think there is still a namespace issue to be resolved? |
Hi, I noticed this bug because of the regression of Python 3.9.8. And I proposed a better approach in PR 29574. Maybe the folks here can have a look. Thanks
|
Per the review comments of @jiasli, I worked out a second PR to fix this issue. This fix has less side-effect and better backward compatibility. I will leave the two PRs open. Any feedback is welcome.
|
On python 3.8.10 (and 3.10), subparsers are not updated with defaults. I suspect this is related to [1]. Fix this by explicitly updating subparsers with settings. [1] python/cpython#89398 Fixes: 3145b63 ("patman: Update defaults in subparsers") Signed-off-by: Sean Anderson <[email protected]> Reviewed-by: Alper Nebi Yasak <[email protected]> Tested-by: Alper Nebi Yasak <[email protected]>
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: