-
-
Notifications
You must be signed in to change notification settings - Fork 31.3k
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
Multiple enum mixins not allowed even when they have the same datatype #88522
Comments
Prior to 3.8 it was possible to create "abstract" enums (without members) and mix them together. To motivate with an example, perhaps we're modeling an API and want to be robust in the face of inconsistent casing class CaseInsensitiveStrEnum(str, Enum):
@classmethod
def _missing_(cls, value):
for member in cls._member_map_.values():
if member._value_.lower() == value.lower():
return member
return super()._missing_(value) and perhaps we also want to be robust in response to extensibility class LenientStrEnum(str, Enum):
@classmethod
def _missing_(cls, value):
logger.warning(
f"[{cls.__name__}] encountered an unknown value!\n"
f"Luckily I'm a LenientStrEnum, so I won't crash just yet.\n"
f"You might want to add a new case though.\n"
f"Value was: '{value}'"
)
return UnexpectedStr(value) but we also want to model some known good set of values, so mixing together the abstract enums we'd get something like class JobStatus(CaseInsensitiveStrEnum, LenientStrEnum):
ACTIVE = "active"
PENDING = "pending"
TERMINATED = "terminated" However, due to the resolution of https://bugs.python.org/issue39587 this no longer works, instead producing: TypeError: 'JobStatus': too many data types: [<class 'str'>, <class 'str'>] The relevant change is I believe that if we made |
Excellent bug report. But what is an |
Sorry, that’s maybe less relevant to the example. It’s just a subclass of string with some marker to make it detectable later on, similar to schemes that taint user input to prevent sql injection or whatever
|
Oh, on further investigation I see that the example wouldn't have worked on 3.8 anyway, due to bpo-34536 adding some checks to the type returned by _missing_, so maybe I'm pushing Enum too far in that case. |
Since I like puzzles, here is a working LenientStrEnum: class LenientStrEnum(str, Enum):
#
def __init__(self, *args):
self._valid = True
#
@classmethod
def _missing_(cls, value):
logger.warning(
f"[{cls.__name__}] encountered an unknown value!\n"
f"Luckily I'm a LenientStrEnum, so I won't crash just yet.\n"
f"You might want to add a new case though.\n"
f"Value was: '{value}'"
)
unknown = cls._member_type_.__new__(cls, value)
unknown._valid = False
unknown._name_ = value.upper()
unknown._value_ = value
cls._member_map_[value] = unknown
return unknown
#
@property
def valid(self):
return self._valid
|
On 10.06.2021 15:33, Ethan Furman wrote:
Oh indeed, that's really cool! |
Glad you like it! Please don't change the title back. :-) |
Oops, still getting used to the Python mailing list format. Learned On 11.06.2021 08:19, Ethan Furman wrote:
|
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: