Skip to content
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 bug: prefix_chars argument to add_argument_group doesn't really work #89819

Open
samwyse mannequin opened this issue Oct 28, 2021 · 3 comments
Open

argparse bug: prefix_chars argument to add_argument_group doesn't really work #89819

samwyse mannequin opened this issue Oct 28, 2021 · 3 comments
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@samwyse
Copy link
Mannequin

samwyse mannequin commented Oct 28, 2021

BPO 45656
Files
  • argparser-bug.py
  • 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:

    assignee = None
    closed_at = None
    created_at = <Date 2021-10-28.18:27:12.120>
    labels = ['type-bug', 'library']
    title = "argparse bug: prefix_chars argument to add_argument_group doesn't really work"
    updated_at = <Date 2021-11-02.20:19:39.416>
    user = 'https://bugs.python.org/samwyse'

    bugs.python.org fields:

    activity = <Date 2021-11-02.20:19:39.416>
    actor = 'paul.j3'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2021-10-28.18:27:12.120>
    creator = 'samwyse'
    dependencies = []
    files = ['50410']
    hgrepos = []
    issue_num = 45656
    keywords = []
    message_count = 3.0
    messages = ['405217', '405525', '405542']
    nosy_count = 2.0
    nosy_names = ['samwyse', 'paul.j3']
    pr_nums = []
    priority = 'normal'
    resolution = None
    stage = None
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue45656'
    versions = ['Python 3.6']

    @samwyse
    Copy link
    Mannequin Author

    samwyse mannequin commented Oct 28, 2021

    Using the prefix_chars argument to parser.add_argument_group causes usage information to print correctly, but the resulting parser doesn't recognize the options. The included program reproduces the issue; it produces the following output on my system.

    --- python version
    3.6.8 (default, Sep 26 2019, 11:57:09)
    [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
    --- test using prefix_chars='-/' with ArgumentParser
    --- show help info
    usage: test-1 [-h] [-foo FOO] [/bar BAR]

    optional arguments:
    -h, --help show this help message and exit
    -foo FOO
    /bar BAR

    --- try parsing something
    Namespace(bar='b', foo='f')
    --- test using prefix_chars='-/' with an argument group
    --- show help info
    usage: test-2 [-h] [-foo FOO] [/bar BAR]

    optional arguments:
    -h, --help show this help message and exit

    other arguments:
    -foo FOO
    /bar BAR

    --- try parsing something
    usage: test-2 [-h] [-foo FOO] [/bar BAR]
    test-2: error: unrecognized arguments: /bar b

    @samwyse samwyse mannequin added stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error labels Oct 28, 2021
    @paulj3
    Copy link
    Mannequin

    paulj3 mannequin commented Nov 2, 2021

    Use of 'prefix_chars' as a argument_group parameter is not documented, nor intended.

    If it does appear to work in the help formatting, it is probably the result of inheritance, since both ArgumentParser and Argument_group subclass a _Actions_Container class. I'd have to examine the code to see what's happening. Argument_groups are used only for help formatting, not for parsing.

    In any case, I don't think anything needs to be changed or fixed.

    @paulj3
    Copy link
    Mannequin

    paulj3 mannequin commented Nov 2, 2021

    prefix_chars is a parameter of the parent _ActionsContainer

    class _ActionsContainer(object):
    
        def __init__(self,
                     description,
                     prefix_chars,
                     argument_default,
                     conflict_handler):
            super(_ActionsContainer, self).__init__()
    
            self.description = description
            self.argument_default = argument_default
            self.prefix_chars = prefix_chars
            self.conflict_handler = conflict_handler

    add_argument is also a method in this class. It uses its own prefix_chars to categorize arguments as optional/positional

            chars = self.prefix_chars
            if not args or len(args) == 1 and args[0][0] not in chars: 

    see also _get_optional_kwargs method.

    class _ArgumentGroup(_ActionsContainer):

    inherits from _ActionsContainer, and usually gets the prefix_chars from its container (the parser)

            update('prefix_chars', container.prefix_chars)

    The parser is created with

    class ArgumentParser(_AttributeHolder, _ActionsContainer):

    and documents the use of prefix_chars.

    In addition to passing prefix_chars to its Super, it uses

        default_prefix = '-' if '-' in prefix_chars else prefix_chars[0]
    

    to define the -h help argument.

    Early in parsing, args strings are categorized as 'O' or 'A'. That is done in, which uses:

        def _parse_optional(self, arg_string):
    
            # if it doesn't start with a prefix, it was meant to be positional
            if not arg_string[0] in self.prefix_chars:
                return None
    
    def _get_option_tuples(self, option_string):

    also uses the parser's own prefix_chars.

    During parsing (several layers down)

        def consume_optional(start_index):
    
                        # if the action is a single-dash option and takes no
                        # arguments, try to parse more single-dash options out
                        # of the tail of the option string
                        chars = self.prefix_chars
                        if arg_count == 0 and option_string[1] not in chars:

    Here, the parser's own prefix_chars is used to handle the chained short-dash options.

    ---

    In sum, while Argument_Group can have its own prefix_chars, that is only used for help formatting. It's the parser's prefix_chars that is used when parsing. The group's chars is ignored.

    The _ArgumentGroup__init__ suggests something more than simple inheritance may have been intended for prefix_chars, but in practice all it affects is the help.

    I haven't seen this issue raised before, and since this group parameter is not documented, I think it's safe to ignore it.

    An alternative is to have add_argument_group (or _ArgumentGroup__init__) explicitly reject the prefix_chars parameter.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
    Projects
    Status: Bugs
    Development

    No branches or pull requests

    0 participants