Skip to content

allow_abbrev=False does not work for single-dash long options #104860

Closed

Description

@SunCrazy

Bug report

The following codes get different results between python3.6.9 and python3.8.10

import argparse


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        allow_abbrev=False
    )

    parser.add_argument('-on', "--output-name", type=str, help="output")

    args, _ = parser.parse_known_args(["-on", "name", "-o", "dir"])
    print(args)

python3.6.9

[chenf@b12e0231:/mnt/ssd/chenf/projects/test_hhb_2.4/debug/cmd_config]$ python3 --version
Python 3.6.9
[chenf@b12e0231:/mnt/ssd/chenf/projects/test_hhb_2.4/debug/cmd_config]$ python3 ~/test.py
Namespace(output_name='name')

python3.8.10

[chenf@b11b0623:/mnt/ssd/chenf/project/hhb_test_2.4/cmd_config]$ python3 --version
Python 3.8.10
[chenf@b11b0623:/mnt/ssd/chenf/project/hhb_test_2.4/cmd_config]$ python3 ~/test.py
Namespace(output_name='dir')

Debug

I have tried to debug the codes and get some clues.

I have set the allow_abbrev=False to disable prefix match, but it works in python3.6.9 and fails in python3.8.10.
When I div into argparse.py and find that it considers the allow_abbrev in python3.6.9 but not in python3.8.10 as follow:

in python3.6.9 argparse.py

def _parse_optional(self, arg_string):
    ....

    if self.allow_abbrev:
            # search through all possible prefixes of the option string
            # and all actions in the parser for possible interpretations
            option_tuples = self._get_option_tuples(arg_string)

            # if multiple actions match, the option string was ambiguous
            if len(option_tuples) > 1:
                options = ', '.join([option_string
                    for action, option_string, explicit_arg in option_tuples])
                args = {'option': arg_string, 'matches': options}
                msg = _('ambiguous option: %(option)s could match %(matches)s')
                self.error(msg % args)

            # if exactly one action matched, this segmentation is good,
            # so return the parsed action
            elif len(option_tuples) == 1:
                option_tuple, = option_tuples
                return option_tuple
    ....

in python3.8.10 argparse.py

def _parse_optional(self, arg_string):
    ....

    # search through all possible prefixes of the option string
        # and all actions in the parser for possible interpretations
        option_tuples = self._get_option_tuples(arg_string)

        # if multiple actions match, the option string was ambiguous
        if len(option_tuples) > 1:
            options = ', '.join([option_string
                for action, option_string, explicit_arg in option_tuples])
            args = {'option': arg_string, 'matches': options}
            msg = _('ambiguous option: %(option)s could match %(matches)s')
            self.error(msg % args)

        # if exactly one action matched, this segmentation is good,
        # so return the parsed action
        elif len(option_tuples) == 1:
            option_tuple, = option_tuples
            return option_tuple
    
    ....

Linked PRs

Activity

  1. added
    pendingThe issue will be closed if no feedback is provided
    on May 24, 2023
  2. JelleZijlstra commented on May 24, 2023

    @JelleZijlstra
    Member

    3.6 is unsupported and 3.8 is only receiving security fixes, so neither version is going to change here.

    However, 3.12 behaves like 3.8 (Namespace(output_name='dir')). Do you think that behavior is buggy? It's not obvious to me which behavior is correct.

  3. SunCrazy commented on May 25, 2023

    @SunCrazy
    Author

    3.6 is unsupported and 3.8 is only receiving security fixes, so neither version is going to change here.

    However, 3.12 behaves like 3.8 (Namespace(output_name='dir')). Do you think that behavior is buggy? It's not obvious to me which behavior is correct.

    Yes, I think that it is a bug. As described in the python document as follows:

    image

    Fuzzy matching should be prohibited while allow_abbrev=False is set. As above codes shows, I set allow_abbrev=False when create argparse obj, ['-o', 'dir'] is still parsed to '-on' and then rewrite the value that set by ['-on', 'name']. That's why the current result is Namespace(output_name='dir').

  4. hmc-cs-mdrissi commented on May 25, 2023

    @hmc-cs-mdrissi
    Contributor

    I think --output-name is long option, while -on is a short option (1 vs 2 hyphens). And I'm confused how two letter short option is intended to be interpreted. It's not being treated as 2 short options of -o + -n because ['-n', 'dir'] does get ignored. While it feels like weird behavior, the bigger question to me is there any documentation for how one hyphen multiple character flag is supposed to behave? Is -on meant to be one 2 letter option or 2 one character options? If it's a 2 letter option what's difference between -on vs --on?

  5. sunmy2019 commented on May 27, 2023

    @sunmy2019
    Member

    Do you think that behavior is buggy?

    I think so. The prefix matching code should be reverted to 3.6 version if we are following what the description is telling us.

  6. ronaldoussoren commented on May 27, 2023

    @ronaldoussoren
    Contributor

    The code in. _parse_optional does not look at allow_abbrev in the current version of argparse, but _get_option_tuples does. That method explicitly only allows for abbreviations for long options, that is those whose name starts with two dashes.

    The same method (_get_option_tuples) also explicitly only looks at the first character for multi-character short option (single dash at the start).

    Current behaviour looks intentional ("-o dir" uses the short option, not an abbrev of the long option), but I'm far from an expert on argparse.

    Regardless of this, the current behaviour is present in 3.8 - 3.12 and changing this can break other users.

  7. hpaulj commented on Jun 3, 2023

    @hpaulj

    Originally abbreviations were allowed. Then we added this parameter to turn that off. But that proved to be buggy, since it broke the chained short option case. That was patched, probably between 3.6 and 3.8.. Feel free to search for the relevant issues and patches.

    So '-o' is allowed as an abbreviation of '-on', hence the 3.8 behavior.

    While '-o' is the "ideal" short option, the distinction between long and short is handled piecemeal. So '-on' qualifies as short (in the abbreviation context), even though it wouldn't be handled as such in the chaining context.

    I suppose we could add a test in add_argument to complain about hybrid shorts like '-on'. But is that change worth it? I say we've done enough fiddling with this abbreviation business. We don't have to close all edge cases. If programmers want to define flags that might confuse their own end users, let them.

    Paul

  8. AA-Turner commented on Aug 13, 2023

    @AA-Turner
    Member

    It seems that this is intended behaviour due to the changes mentioned above.

    A

  9. removed
    pendingThe issue will be closed if no feedback is provided
    on Aug 13, 2023
  10. sunmy2019 commented on Aug 13, 2023

    @sunmy2019
    Member

    It seems that this is intended behaviour due to the changes mentioned above.

    No. This change is unexpected and problematic.

    It's just that nobody is maintaining this library, and no one is willing to fix this bug for compatibility reasons.

  11. 4 remaining items

  12. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Nov 26, 2023
  13. serhiy-storchaka commented on Sep 23, 2024

    @serhiy-storchaka
    Member

    This is not intentional behavior, but a bug that was introduced in process of fixing flag grouping with allow_abbrev=False, presumably in dffca9e (bpo-26967).

    -on is not a short option, it is a single-dash long option. It can be abbreviated to -o if allow_abbrev=True (default) and cannot be grouped with short options. allow_abbrev=False should disallow abbreviation, but currently it does not work for single-dash long options.

  14. added
    3.12only security fixes
    3.13only security fixes
    3.14bugs and security fixes
    on Sep 23, 2024
  15. added a commit that references this issue on Sep 23, 2024
  16. changed the title [-]parse different results with argparse between python3.6.9 and python3.8.10[/-] [+]allow_abbrev=False does not work for single-dash long options[/+] on Sep 26, 2024
  17. added a commit that references this issue on Sep 29, 2024
  18. added 2 commits that reference this issue on Sep 29, 2024
  19. moved this from Bugs to Doc issues in Argparse issueson Sep 29, 2024
  20. added a commit that references this issue on Sep 29, 2024
  21. added a commit that references this issue on Oct 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

3.12only security fixes3.13only security fixes3.14bugs and security fixesstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions