-
Notifications
You must be signed in to change notification settings - Fork 3
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
"User friendly" enums #1181
Comments
I like the idea. Some of my thoughtsIn the first code snippet you define the Color class as What i gather from the python documentation calling the class like that is called the function-call syntax for creating an enum. Its an alternative way to the class defintition you use. It isn't used to create a specific enum member Preferably the new Enum should be a drop in replacement of the original enum. I do like your |
You're right: I got my first example mixed up, what I should/could have written is: class Color(Enum):
RED = "RED"
GREEN = "GREEN"
BLUE = "BLUE" This does accept This works:
But is kind of anti-feature for our usage.
This is doable, although not that easy. It needs a metaclass. Adding a Here's basic implementation: from enum import Enum, EnumMeta
from typing import Any, Type, TypeVar, Union
E = TypeVar("E", bound="FlexibleEnum")
def _show_options(options: Type[E]) -> str:
return "\n * ".join(map(str, options.__members__))
class AttributeErrorMeta(EnumMeta):
def __getattr__(cls, name: str) -> Any:
try:
return cls.__members__[name]
except KeyError:
raise AttributeError(
f"{name} is not a valid {cls.__name__}. "
f"Valid options are:\n * {_show_options(cls)}"
)
class FlexibleEnum(Enum, metaclass=AttributeErrorMeta):
@classmethod
def parse(cls: Type[E], value: str) -> E:
try:
return cls.__members__[value]
except KeyError:
raise ValueError(
# Use __repr__() so strings are shown with quotes.
f"{value.__repr__()} is not a valid {cls.__name__}. "
f"Valid options are:\n * {_show_options(cls)}"
) |
Nice! I just tested this:
Which gives a clear error message. I think it is fine that a metaclass is needed to get this output. |
FYI, I've included an implementation in pandamesh here: https://github.com/Deltares/pandamesh/blob/main/pandamesh/enum_base.py I've maintained the |
I was also struggling with rendering the enums docs in an appropriate manner, I needed a custom template in the end: https://github.com/Deltares/pandamesh/blob/main/docs/_templates/enums.rst This is included in the index:
|
Basically a follow up of #416:
We've briefly discussed this before: Enums are a great way to enumerate options, but a lot of our users aren't familiar with them. The issue with enums is also that you need to import the relevant enums from the right namespace. A pragmatic solution is to dynamically force inputs to enums, thereby checking them as well.
This gives decent errors:
Ideally, it wouldn't tell you that it's wrong, but what the right entries are. This is especially helpful with typos.
Another advantage is that regular enums accept integer values.
E.g. one of the current enums:
In general, I don't think we want user facing functions to support something like
.allocate(option=0)
. A default Enum will supportALLOCATION_OPTION(0)
.But with the
FlexibleEnum.from_value()
, it won't (which is good):So my suggestion is to replace the Enums with these FlexibleEnums (or a better name), and preferable the same for all strings literals that we support. Then inside of the function:
This ensures the option is validated and that a clear error message listing the available valid options is printed.
The text was updated successfully, but these errors were encountered: