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: embedded groups may prevent options from being in help output #90215
Comments
I tried to use the following code where the --db-password is not shown in the --help output (Originally I wanted to use mutually exclusive groups but that feature also works strangely, so I changed them to regular groups). def register_db_args(parser: argparse.ArgumentParser):
grp = parser.add_argument_group('Database settings')
grp.add_argument('--db-config', dest='db_config_file',
help='Config file containg all details including password')
grp.add_argument('--db-host')
grp.add_argument('--db-port')
grp.add_argument('--db-user')
xgrp = grp.add_argument_group()
xgrp.add_argument('--db-password')
xgrp.add_argument('--db-password-env')
xgrp.add_argument('--db-password-file') |
Please complete the bug report: How did you run this function, what output did you get and what output did you expect? |
Sorry, these are two bugs in fact. The current one, the help with minmal code: import argparse
parser = argparse.ArgumentParser()
grp = parser.add_argument_group('Database settings')
grp.add_argument('--db-config')
xgrp = grp.add_argument_group()
xgrp.add_argument('--db-password')
parser.parse_args(['-h']) The group's help output shows only --db-config option: Database settings: If I change the xgrp to be mutually exclusive group as: then the output is the same as I expect for the previous code, too: Database settings: |
And the leading part is the same for both the mutually exclusive and the argument groups: usage: test1.py [-h] [--db-config DB_CONFIG] [--db-password DB_PASSWORD] optional arguments: Database settings: |
The fix is something like this for _ArgumentGroup, but as the container may not be an _ArgumentGroup, it breaks the tests. --- Lib/argparse.py
+++ Lib/argparse.py
@@ -1635,9 +1640,13 @@ def __init__(self, container, title=None, description=None, **kwargs):
self._has_negative_number_optionals = \
container._has_negative_number_optionals
self._mutually_exclusive_groups = container._mutually_exclusive_groups
+ self._container = container
def _add_action(self, action):
- action = super(_ArgumentGroup, self)._add_action(action)
+ if self.title:
+ action = super(_ArgumentGroup, self)._add_action(action)
+ else:
+ action = self._container._add_action(action)
self._group_actions.append(action) |
According to the docs it should be
rather than
This seems to work: >>> parser = argparse.ArgumentParser()
>>> grp = parser.add_argument_group('Database settings')
>>> grp.add_argument('--db-config')
_StoreAction(option_strings=['--db-config'], dest='db_config', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
>>> xgrp = parser.add_argument_group()
>>> xgrp.add_argument('--db-password')
_StoreAction(option_strings=['--db-password'], dest='db_password', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
>>> parser.parse_args(['-h'])
usage: [-h] [--db-config DB_CONFIG] [--db-password DB_PASSWORD] options: Database settings: --db-password DB_PASSWORD |
According to the documentation only the ArgumentParser has add_argument_group option, which is not true, the code allows me to add a subgroup to any group. The complete example is in bpo-4608: https://bugs.python.org/issue46058 If add_argument_group shouldn't be used for a regular argument group, This is close to the originally intended, documented behaviour. I'd still allow groups without title in mutually exclusive groups and vice versa. |
my idea regarding _ArgumentGroup,add_argument_group is in the attached file. This doesn't solve the complete help output issue, but addresses the incorrectly called _ArgumentGroup.add_argument_group - a warn() call and return self. |
You’re right that the api should not be there. See bpo-22047. I don’t think it should be patches to call super/return self. That would just be confusing. |
Nesting argument groups and mutually exclusive groups is now deprecated (see bpo-22047). Thank you for the bug report. |
Don't add an argument_group to another argument_group. While input allows this nesting, the formatting is not designed to handle it. Nor does the documentation illustrate such nesting. Nesting a mutually_exclusive_group in an argument_group works because the exclusive_group is not used in formatting the help lines. The two class of groups have very different purposes. Note that the add_argument_group() method is defined for the parent _ActionsContainer class. ArgumentParser inherits from this, as do both of the group classes. While one could make a case for changing the group's inheritance of this method to Not-implemented, it's only a problem when users, like you, try to push for undocumented usage. In general argparse has a clean and powerful class structure. But it doesn't do a lot of checking and pruning, so there loose ends like this. The Action class and its subclasses is similarly powerful, with enough loose ends to allow adventurous users to hang themselves :). |
I don't think I'm adventurous as I try to use its public API. If something is public in the pythonic terms (no underscore before it), but undocumented, that can obviously mean two things: either the documentation is outdated or the API published something it shouldn't. As I wrote in bpo-46058 - which is the same issue as this because of the implementation in argparse.py - I tried to create a group hierarchy in a way that I can pass the responsibility of argument validation to the argument parser. I don't think the good practice is to reimplement the same behavior again and again in dozens of Python programs if it is something that could be handled in an argument parser. My original intent was to provide patch or patches fixing the issue but with the deprecation in bpo-22047 this became pointless. There were a few other opened issues indicating I'm not alone with this expectation. |
"I tried to create a group hierarchy in a way that I can pass the responsibility of argument validation to the argument parser." I looked at your example in: https://bugs.python.org/issue46058 The many levels of nesting groups was confusing, but now I realize that by nesting argument_groups in a mutually_exlusive_group you were try to implement some sort of any/all group within the xor group. This cannot be done within argparse. Mutually_exclusive only implements a flat xor. Argument_groups are used for help display, but play no role in parsing. Some years ago I explored the use of nest parsing groups: https://bugs.python.org/issue11588 I was trying to allow for nested groups that implemented all logical options - xor, or, and, not. Defining such groups wasn't hard. And I think I got the testing logic working right. But usage display required too big of a change to the formatter, and left too many loose ends. So I have let that languish. Now I recommend focusing on doing the testing after parsing. Use meaningful defaults where possible, and use |
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: