lebrice / SimpleParsing

Simple, Elegant, Typed Argument Parsing with argparse
MIT License
429 stars 52 forks source link

Show possible choices for enums #65

Open rggjan opened 3 years ago

rggjan commented 3 years ago

In the example here: https://github.com/lebrice/SimpleParsing/tree/master/examples/enums

Calling args = parser.parse_args(["--help"])

gives: --color Color my favorite colour (default: BLUE)

It would be nice to get a list of possible choices here, something like: --color Color my favorite colour {RED, ORANGE, BLUE} (default: BLUE)

gschwaer commented 2 years ago

Is this feature request accepted / is this desired? If I understand this issue correctly, the main problem is that SimpleParsing differentiates (maybe unintentionally) between choices and enum options.

For choices the metavar (the custom name that is used after the argument name) is dropped:

https://github.com/lebrice/SimpleParsing/blob/016d306c3ce7919eba3341cfb26e9509357b3301/simple_parsing/wrappers/field_wrapper.py#L200-L201

For enums that does not happen, instead the metavar still contains the enum type name. Argparse will use the metavar if it exists. Only if it does not exist, it will use the choices for the help string formatting.

I tried dropping the metavar for enums as well and wrote this test in test/test_base.py:

def test_help_displays_color_choices_text():
    help_text = Extended.get_help_text()
    for color in Color:
        assert color.value in help_text

The help_text looks like this:

usage: testlauncher.py [-h] -a int [-b float] [-c str] [-d int]
                       [-e {RED,ORANGE,BLUE}]

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

Extended ['extended']:
  Some extension of base-class `Base`

  -a int, --a int       docstring for attribute 'a' (default: None)
  -b float, --b float   inline comment on attribute 'b' (default: 5.0)
  -c str, --c str       (default: )
  -d int, --d int       docstring for 'd' in Extended. (default: 5)
  -e {RED,ORANGE,BLUE}, --e {RED,ORANGE,BLUE}
                        (default: BLUE)

Not sure though if the metavar is required for some other purpose, I only looked into this on the surface.

rggjan commented 1 year ago

Nice analysis! Yes, this looks much better, would be great to see what can be set. Usually, enums are like a choice with a limited set of options, no?

rggjan commented 1 year ago

@lebrice or is there a specific use case where we don't want to treat an Enum as a choice?

erik-buchholz commented 3 weeks ago

Is there any update on this? I also think that this would be very helpful.

As a workaround, this behaviour can be enforced by manually adding metadata={"custom_args": {"meta var": None}} as an argument to the enum's dataclass field.

@dataclass
class Config:
    enum_type: CustomEnum = field(default=CustomEnum.A, metadata={"custom_args": {"metavar": None}})

However, this seems not to work if you want to use pydantic.Field instead of dataclasses.field such that I still think that the proposed modification by @gschwaer would be worth including. Moreover, this workaround requires annotating each enum individually.