-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
argparse: subparsers, argument abbreviations and ambiguous option #58573
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
Comments
Assuming following:
parsing fails with "error: ambiguous option: --foo could match --foo1, --foo2". It seems like argument abbreviation mechanism described at http://docs.python.org/library/argparse.html#argument-abbreviations is not working properly when in use with subparsers... |
More or less a duplicate of 12713? |
Sorry, I meant bpo-12713. |
Yep. Closing as duplicate. |
I don't understand how both bugs are related. Surely, patch provided for bpo-12713 does not fix the issue I described. |
My mistake. I see that the error you're getting is a bad interaction between the option in the main parser and an ambiguous option in the subparser. |
The problem is basically that _parse_known_args calls _parse_optional to determine whether something is an optional or a positional. But _parse_optional only checks "if arg_string in self._option_string_actions", that is, if the string can be found in the main parser actions. So either this method needs to check the subparser actions, or the "if arg_string in self._option_string_actions" stuff needs to be delayed until the subparser. Neither of these seems like a simple change, but I agree it's a bug. |
Attached quick&dirty fix I'm currently using in my project. Maybe this will help to create a valid patch. Instead of changing _parse_optional, I've overridden _get_option_tuples to scan subparsers for matching optional arguments if option is ambiguous in the current parser. I've only skimmed through the code, so forgive me if that fix is useless. |
I think the real fix needs to search recursively though all subparsers. Here's a first draft of a patch that does this. I believe your sample code works after this patch. I don't have the time to turn your bug report into proper test (and add a test for the recursive bit), but hopefully this is enough to let someone else finish up fixing this bug. And yes, your "dirty hack" was still helpful in putting this together. ;-) |
Steven's patch (subparse_optionals.diff) run with jakub's test case (argparse_subparses_ambiguous_bug.py) works. But if the input string is print(parser.parse_args('--foo baz'.split())) produces Namespace(cmd=None, foo='baz', foo1=None, foo2=None) (I added the 'cmd' subparse 'dest'). Two things seem to be going on now:
The issue of whether subparsers are required or not is another issue. They used to be required, but the testing for 'required' was changed, and subparsers fell through the crack. I suspect that if the missing subparser error were raised, the first issue wouldn't be apparent. |
I think the correction to the problem that I noted in the previous post is to return 'None, arg_string, None', rather than 'action, arg_string, None' in the case where the action is found in a subparser. This is what '_parse_optional' does at the end: # it was meant to be an optional but there is no such option
# in this parser (though it might be a valid option in a subparser)
return None, arg_string, None An input like '--foo baz' would then produce an 'invalid choice' error. Since '--foo' is an optional that the primary parser does not recognize, 'baz' in interpreted as a positional, in this case an invalid subparser choice. I'm working on cleaning up a test script. |
In the last patch, 'parser.scan = True' is needed to activate this fix. I left that switch in for testing convenience. |
This the argparse patch as described in the previous post, along with test_argparse tests. For now the tests handle both subparser required cases and not required ones ( http://bugs.python.org/issue9253 ). While error messages can differ based on this requirement, it does not affect the 'ambiguity' that underlies the current issue. |
Another issue dealing with abbreviations is close to being committed. http://bugs.python.org/issue14910 |
It's 2022 and I'm still affected by this in Python 3.10. import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--flagA", type=str)
parser.add_argument("--flagB", type=str)
subparser = parser.add_subparsers(title="command")
build_parser = subparser.add_parser("build")
build_parser.add_argument("--flag", type=str)
parser.parse_args(["build", "--flag=asd"]) Raises
The fix is to set parser = argparse.ArgumentParser(allow_abbrev=False) |
…(comment)) PiperOrigin-RevId: 477165673
…(comment)) PiperOrigin-RevId: 477169487
… parent parser and subparsers in argparse Check for ambiguous options if the option is consumed, not when it is parsed.
#124631 fixes this issue by delaying the check for ambiguous options until the option been consumed. The option for subparser is consumed by the subparser, not the parent parser, so it is no longer error. |
…t parser and subparsers in argparse (GH-124631) Check for ambiguous options if the option is consumed, not when it is parsed.
… parent parser and subparsers in argparse (pythonGH-124631) Check for ambiguous options if the option is consumed, not when it is parsed. (cherry picked from commit 3f27153) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
… in the parent parser and subparsers in argparse (pythonGH-124631) Check for ambiguous options if the option is consumed, not when it is parsed. (cherry picked from commit 3f27153) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
In Python 3.13 and 3.12.7, the behavior of _parse_optional has changed. It used to raise an error on multiple matching actions itself, and only ever return None or an option tuple. Now the "raise error on multiple matching actions" code was moved out into consume_optional, and _parse_optional returns either None or a *list* of option tuples, which contains more than one if multiple actions match. See: python/cpython#124631 python/cpython#58573 This adapts to the change in a way that should work on both older and newer Pythons. I re-implemented the handling of multiple matching options so we don't have to worry about licensing and attribution if we copied in upstream's version. Signed-off-by: Adam Williamson <awilliam@redhat.com>
In Python 3.13 and 3.12.7, the behavior of _parse_optional has changed. It used to raise an error on multiple matching actions itself, and only ever return None or an option tuple. Now the "raise error on multiple matching actions" code was moved out into consume_optional, and _parse_optional returns either None or a *list* of option tuples, which contains more than one if multiple actions match. See: python/cpython#124631 python/cpython#58573 This adapts to the change in a way that should work on both older and newer Pythons. Signed-off-by: Adam Williamson <awilliam@redhat.com>
In Python 3.13 and 3.12.7, the behavior of _parse_optional has changed. It used to raise an error on multiple matching actions itself, and only ever return None or an option tuple. Now the "raise error on multiple matching actions" code was moved out into consume_optional, and _parse_optional returns either None or a *list* of option tuples, which contains more than one if multiple actions match. See: python/cpython#124631 python/cpython#58573 This adapts to the change in a way that should work on both older and newer Pythons. Signed-off-by: Adam Williamson <awilliam@redhat.com>
In Python 3.13 and 3.12.7, the behavior of _parse_optional has changed. It used to raise an error on multiple matching actions itself, and only ever return None or an option tuple. Now the "raise error on multiple matching actions" code was moved out into consume_optional, and _parse_optional returns either None or a *list* of option tuples, which contains more than one if multiple actions match. See: python/cpython#124631 python/cpython#58573 This adapts to the change in a way that should work on both older and newer Pythons. Signed-off-by: Adam Williamson <awilliam@redhat.com>
In Python 3.13 and 3.12.7, the behavior of _parse_optional has changed. It used to raise an error on multiple matching actions itself, and only ever return None or an option tuple. Now the "raise error on multiple matching actions" code was moved out into consume_optional, and _parse_optional returns either None or a *list* of option tuples, which contains more than one if multiple actions match. See: python/cpython#124631 python/cpython#58573 This adapts to the change in a way that should work on both older and newer Pythons. NOTE: This includes the fix for the typo that is in a separate commit on master. Signed-off-by: Adam Williamson <awilliam@redhat.com> Signed-off-by: Brian C. Lane <bcl@redhat.com> Resolves: RHEL-61426
This was a regression introduced in pythongh-58573. It was only tested for the case when the ambiguous option is the last argument in the command line.
…ythonGH-125273) This was a regression introduced in pythongh-58573. It was only tested for the case when the ambiguous option is the last argument in the command line. (cherry picked from commit 63cf4e9) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
…ythonGH-125273) This was a regression introduced in pythongh-58573. It was only tested for the case when the ambiguous option is the last argument in the command line. (cherry picked from commit 63cf4e9) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
…GH-125273) (GH-125360) This was a regression introduced in gh-58573. It was only tested for the case when the ambiguous option is the last argument in the command line. (cherry picked from commit 63cf4e9) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
…GH-125273) (GH-125359) This was a regression introduced in gh-58573. It was only tested for the case when the ambiguous option is the last argument in the command line. (cherry picked from commit 63cf4e9) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
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:
Linked PRs
The text was updated successfully, but these errors were encountered: