-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Choice
prefers Enum.name
even for StrEnum
where it should use the value
#2911
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
Comments
Could this be the culprit: https://github.com/pallets/click/pull/2796/files#diff-dcb534e6a7591b92836537d4655ddbd2f18e3b293c3420144c30a9ca08f65c4eR298 ? And if so, is this behaviour intentional? If not, I'd be happy to open a PR with an additional test for |
Choice
in Click 8.2.0 stringifies enums by name
, even when they're str
subclassesChoice
stringifies enums by name
, even when they're str
subclasses
I've replicated the example as a test and I guess the follow-up question is whether this default is desirable. I could see quite a few class SomeStrEnum(StrEnum)
FOO = "foo"
BAR = "bar"
class SomeOtherStrEnum(str, Enum)
BLA = "bla"
ETC = "etc" that are currently being used in We experienced a similar, but not exactly the same, issue for @dataclass
class CLIOptions
foo_preference: Annotated[
SomeStrEnum,
typer.Option( "-f", "--foo-preference"),
] = SomeStrEnum.FOO broke as EDIT: Looping in @tiangolo in case reports of scenarios above come in for Typer as well and the click connection isn't immediately clear |
|
Just guessing based on the default value and the way the function is written. Apologies, I should not have stated it so definitively. I am not a developer on |
@qdegraaf #2796 added support for A good solution would perhaps be to get the starting value in def normalize_choice(self, choice: ParamTypeValue, ctx: Context | None) -> str:
"""
Normalize a choice value, used to map a passed string to a choice.
Each choice must have a unique normalized value.
By default uses :meth:`Context.token_normalize_func` and if not case
sensitive, convert it to a casefolded value.
.. versionadded:: 8.2.0
"""
- normed_value = choice.name if isinstance(choice, enum.Enum) else str(choice)
+ match choice:
+ case str(choice):
+ normed_value = choice
+ case Enum(choice):
+ normed_value = choice.name
+ case _:
+ normed_value = str(choice)
if ctx is not None and ctx.token_normalize_func is not None:
normed_value = ctx.token_normalize_func(normed_value)
if not self.case_sensitive:
normed_value = normed_value.casefold()
return normed_value |
Choice
stringifies enums by name
, even when they're str
subclassesChoice
prefers Enum.name
even for StrEnum
where it should use the value
Thanks for clarifying! Because the tests used a In the case of a class that is both assert "Usage: cli [OPTIONS] {foo-value|bar-value|baz-value}\nTry 'cli --help' for help.\n\nError: Invalid value for '{foo-value|bar-value|baz-value}': 'foo' is not one of 'foo-value', 'bar-value', 'baz-value'.\n" == 'foo-value\n' Which makes sense of course, but could undermine the original intention. |
Working on this (PyCon 2025) |
I don't like the idea of using |
@davidism What about just using |
That will still result in unexpected inconsistencies in enums. I'm marking this as a documentation fix rather than something we'll change. If custom behavior is needed, then a custom type or callback can be written to do exactly what that application needs. |
Consider the following code:
With Click 8.1.8, running this script with
-C red
works as expected, but with Click 8.2.0, it instead fails with:This was observed with Python 3.13.3 on macOS 14.7.5.
The text was updated successfully, but these errors were encountered: