Skip to content

Argparse: certain action types dont accept metavar, but should? #109792

Closed
@DanCardin

Description

@DanCardin

Feature or enhancement

Proposal:

Given some arbitrary argument with a dest value of foo.bar.baz (justification below), you will end up with helptext like FOO.BAR.BAZ. This is the explicit usecase for the use of metavar. You set metavar to the value you want it to show up as, and you're good.

Unfortunately, you aren't allowed to supply metavar in some scenarios. I haven't enumerated all of them yet, but, for example, if you set action='help', action='store_true', or action='store_false', you'll be routed through _HelpAction, _StoreTrueAction, or _StoreFalseAction, none of which accept a metavar argument. There may be more.

The superclasses of all three do accept metavar, but in usercode i can just subclass them like so:

class _HelpAction(argparse._HelpAction):
    def __init__(self, metavar=None, **kwargs):
        self.metavar = metavar
        super().__init__(**kwargs)


class _StoreTrueAction(argparse._StoreTrueAction):
    def __init__(self, metavar=None, **kwargs):
        self.metavar = metavar
        super().__init__(**kwargs)


class _StoreFalseAction(argparse._StoreFalseAction):
    def __init__(self, metavar=None, **kwargs):
        self.metavar = metavar
        super().__init__(**kwargs)

class ArgumentParser(argparse.ArgumentParser):
    def __init__(self, *args, **kwargs):
        self.register("action", "store_true", _StoreTrueAction)
        self.register("action", "store_false", _StoreFalseAction)

With no other changes, the rendered helptext names of the options properly accept and render the metavar value instead of the dest.


Some context/justification for why this is meaningful. I am currently using this snippet from the internet to, with relatively little effort, "properly" parse multiply nested subparsers in a way that maintains the nested relationship of the input args when they're written:

class Nestedspace(argparse.Namespace):
    def __setattr__(self, name, value):
        if "." in name:
            group, name = name.split(".", 1)
            ns = getattr(self, group, Nestedspace())
            setattr(ns, name, value)
            self.__dict__[group] = ns
        else:
            self.__dict__[name] = value

Which produces {'foo': {'bar': {'baz': 4}}} for some doubly nested subparser with a baz argument. Whereas it'd have just produced {'baz': 4} by "default".

This strategy requires setting the dest value to foo.bar.baz for the arg in question.

Having done that, you end up with [-k FOO.BAR.BAZ] in the help text, with no way to customize it.


Has this already been discussed elsewhere?

This is a minor feature, which does not need previous discussion elsewhere

Links to previous discussion of this feature:

I dont know whether this is relevant to issues like #103678, where they're removing metavar from not-these-classes-but-boolean-action-option, which naively seems related. perhaps it's unrelated though.


if it were just a matter of making the in-repo equivalents to these changes (plus tests), i'd be willing to contribue them, but i figured it might be this was for a reason, and wanted to make sure it'd be accepted before submitting a PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    pendingThe issue will be closed if no feedback is providedstdlibPython modules in the Lib dirtype-featureA feature request or enhancement

    Projects

    Status

    Doc issues

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions