Thanks to @hpaulj answer, I had a proper look at the argparse internals and came up with the following. While it has some caveats, I feel it's a good start:
class ModularHelpEnabler(argparse.Action):
def __call__(self, parser, namespace, values, option_string = None):
parser.enable_modular_help(self.const)
class ModularArgumentParser(argparse.ArgumentParser):
def __init__(self, *args, **kwargs):
self._modular_action_groups = {}
modular_help_groups = kwargs.pop('modular_help_groups', [])
super().__init__(*args, **kwargs)
self._modular_help_groups = {}
for name in modular_help_groups:
self._modular_help_groups[name] = self.add_argument_group(name)
def add_argument_group(self, *args, **kwargs):
name = kwargs.pop('help_name', None)
help_group = kwargs.pop('help_group', None)
help_text = kwargs.pop('help_group', 'show help for {}')
grp = super().add_argument_group(*args, **kwargs)
if name is not None:
self._modular_action_groups[grp] = name
parser = self
if help_group is not None:
parser = self._modular_help_groups[help_group]
parser.add_argument('--help-{}'.format(name), action=ModularHelpEnabler, nargs=0, const=grp, help = help_text.format(name))
return grp
def enable_modular_help(self, grp):
del self._modular_action_groups[grp]
def format_help(self):
self._action_groups = [
ag for ag in self._action_groups
if ag not in self._modular_action_groups
]
return super().format_help()
Caveats (that are not trivial to fix, AFAIK):
--help-foo does not imply --help.
--help-foo needs to be given before --help.
Unfortunately, --help directly triggers printing the help and exits. If we retain this behavior, everything that modifies the help message needs to be specified before --help itself.
We could defer printing the help until after parsing has finished, however this would mean that --help no longer shadows parsing errors (as it does right now), changing the current behavior significantly.
print_helpandformat_help. You'll see how theHelpFormatteris called, and how the groups are passed to it. Also look at the_help_actionclass (or some such name). I can imagine creating similar Action subclasses and/or formatters that display a subset of yourargument_groups.