typer
typer copied to clipboard
Using typer.prompt with choices from Enum doesn't display choices
First Check
- [X] I added a very descriptive title to this issue.
- [X] I used the GitHub search to find a similar issue and didn't find it.
- [X] I searched the Typer documentation, with the integrated search.
- [X] I already searched in Google "How to X in Typer" and didn't find any information.
- [X] I already read and followed all the tutorial in the docs and didn't find an answer.
- [X] I already checked if it is not related to Typer but to Click.
Commit to Help
- [X] I commit to help with one of those options 👆
Example Code
import typer
from enum import Enum
class ChoiceEnum(Enum):
option_one = "opt1"
option_two = "opt2"
option_three = "opt3"
app = typer.Typer()
@app.command()
def subcommand(argument_option: ChoiceEnum = typer.Option(ChoiceEnum.option_two.value,
prompt="argument option",
show_choices=True)):
prompt_option: ChoiceEnum = typer.prompt("prompt option",
ChoiceEnum.option_three.value,
show_choices=True)
print(f"Argument choice: {argument_option}")
print(f"Prompt choice: {prompt_option}")
if __name__ == "__main__":
app()
# The result of running the above script results in the following output:
#
# > argument option (opt1, opt2, opt3) [opt2]:
# > prompt option [opt3]:
# > Argument choice: ChoiceEnum.option_two
# > Prompt choice: opt3
Description
Using the typer.prompt
function isn't displaying the choices of an enum the same way that an Option does.
Am I doing something wrong here? Is there some other way I should be doing the prompt in order for it to display the choices as is done with the argument choices?
Also, why are the return values different (the prompt gives me the string "opt3", while the argument returns the enum value itself).
Operating System
macOS
Operating System Details
No response
Typer Version
0.6.1
Python Version
Python 3.10.8
Additional Context
No response
Managed to figure out that I needed to add type=ChoiceEnum
to the prompt call to get the return value to be of the same type as the argument. Still doesn't fix showing the choices.
Managed to find a workaround for this by not using an enum and instead use the click.Choice
type:
click_choice = click.Choice(['opt1a', 'opt2a', 'opt3a'])
click_prompt_option: click.Choice = typer.prompt("click prompt option",
"opt1a",
show_choices=True,
type=click_choice)
Still think that the typer
behavior needs to be looked at to make the behavior a bit more predictable. Would think that label should be updated to bug
.
I solved the issue by creating a new Choice
class for (Int)Enum
s:
class IntEnumChoice(click.Choice):
def __init__(self, enum_type: EnumType, case_sensitive: bool = True) -> None:
choices = [f"{value.value}: {value.name}" for value in enum_type]
super().__init__(choices, case_sensitive)
self._enum_type = enum_type
def convert(self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"]) -> t.Any:
try:
return self._enum_type(int(value))
except ValueError:
choices_str = ", ".join(map(repr, self.choices))
self.fail(
ngettext(
"{value!r} is not {choice}.",
"{value!r} is not one of {choices}.",
len(self.choices),
).format(value=value, choice=choices_str, choices=choices_str),
param,
ctx,
)
You can easily adapt it for string Enum
s (just remove the type conversion to int
in the return self._enum_type(int(value))
line).
I'm looking for a typer
way to replace:
parser.add_argument('move', choices=['rock', 'paper', 'scissors'])
It would be great to have a similar feature in the core of typer
.