Skip to content

Commit b39d524

Browse files
committed
remove custom Enums and use Enum._missing_ for missing values
1 parent bc48553 commit b39d524

File tree

1 file changed

+22
-113
lines changed

1 file changed

+22
-113
lines changed

discord/enums.py

Lines changed: 22 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525

2626
from __future__ import annotations
2727

28+
from enum import Enum as EnumBase
2829
import types
29-
from collections import namedtuple
30-
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, Union
30+
from typing import Any, Self, TypeVar, Union
3131

3232
__all__ = (
3333
"Enum",
@@ -83,118 +83,27 @@
8383
)
8484

8585

86-
def _create_value_cls(name, comparable):
87-
cls = namedtuple(f"_EnumValue_{name}", "name value")
88-
cls.__repr__ = lambda self: f"<{name}.{self.name}: {self.value!r}>"
89-
cls.__str__ = lambda self: f"{name}.{self.name}"
90-
if comparable:
91-
cls.__le__ = lambda self, other: isinstance(other, self.__class__) and self.value <= other.value
92-
cls.__ge__ = lambda self, other: isinstance(other, self.__class__) and self.value >= other.value
93-
cls.__lt__ = lambda self, other: isinstance(other, self.__class__) and self.value < other.value
94-
cls.__gt__ = lambda self, other: isinstance(other, self.__class__) and self.value > other.value
95-
return cls
96-
97-
98-
def _is_descriptor(obj):
99-
return hasattr(obj, "__get__") or hasattr(obj, "__set__") or hasattr(obj, "__delete__")
100-
101-
102-
class EnumMeta(type):
103-
if TYPE_CHECKING:
104-
__name__: ClassVar[str]
105-
_enum_member_names_: ClassVar[list[str]]
106-
_enum_member_map_: ClassVar[dict[str, Any]]
107-
_enum_value_map_: ClassVar[dict[Any, Any]]
108-
109-
def __new__(cls, name, bases, attrs, *, comparable: bool = False):
110-
value_mapping = {}
111-
member_mapping = {}
112-
member_names = []
113-
114-
value_cls = _create_value_cls(name, comparable)
115-
for key, value in list(attrs.items()):
116-
is_descriptor = _is_descriptor(value)
117-
if key[0] == "_" and not is_descriptor:
118-
continue
119-
120-
# Special case classmethod to just pass through
121-
if isinstance(value, classmethod):
122-
continue
123-
124-
if is_descriptor:
125-
setattr(value_cls, key, value)
126-
del attrs[key]
127-
continue
128-
129-
try:
130-
new_value = value_mapping[value]
131-
except KeyError:
132-
new_value = value_cls(name=key, value=value)
133-
value_mapping[value] = new_value
134-
member_names.append(key)
135-
136-
member_mapping[key] = new_value
137-
attrs[key] = new_value
138-
139-
attrs["_enum_value_map_"] = value_mapping
140-
attrs["_enum_member_map_"] = member_mapping
141-
attrs["_enum_member_names_"] = member_names
142-
attrs["_enum_value_cls_"] = value_cls
143-
actual_cls = super().__new__(cls, name, bases, attrs)
144-
value_cls._actual_enum_cls_ = actual_cls # type: ignore
145-
return actual_cls
146-
147-
def __iter__(cls):
148-
return (cls._enum_member_map_[name] for name in cls._enum_member_names_)
149-
150-
def __reversed__(cls):
151-
return (cls._enum_member_map_[name] for name in reversed(cls._enum_member_names_))
152-
153-
def __len__(cls):
154-
return len(cls._enum_member_names_)
155-
156-
def __repr__(cls):
157-
return f"<enum {cls.__name__}>"
86+
class Enum(EnumBase):
87+
"""An :class:`enum.Enum` subclass that implements a missing value creation behavior if it is
88+
not present in any of the members of it.
89+
"""
15890

159-
@property
160-
def __members__(cls):
161-
return types.MappingProxyType(cls._enum_member_map_)
162-
163-
def __call__(cls, value):
164-
try:
165-
return cls._enum_value_map_[value]
166-
except (KeyError, TypeError) as e:
167-
raise ValueError(f"{value!r} is not a valid {cls.__name__}") from e
168-
169-
def __getitem__(cls, key):
170-
return cls._enum_member_map_[key]
171-
172-
def __setattr__(cls, name, value):
173-
raise TypeError("Enums are immutable.")
174-
175-
def __delattr__(cls, attr):
176-
raise TypeError("Enums are immutable")
177-
178-
def __instancecheck__(self, instance):
179-
# isinstance(x, Y)
180-
# -> __instancecheck__(Y, x)
181-
try:
182-
return instance._actual_enum_cls_ is self
183-
except AttributeError:
184-
return False
185-
186-
187-
if TYPE_CHECKING:
188-
from enum import Enum
189-
else:
190-
191-
class Enum(metaclass=EnumMeta):
192-
@classmethod
193-
def try_value(cls, value):
194-
try:
195-
return cls._enum_value_map_[value]
196-
except (KeyError, TypeError):
197-
return value
91+
@classmethod
92+
def _missing_(cls, value: Any) -> Self:
93+
name = f"unknown_{value}"
94+
if name in cls.__members__:
95+
return cls.__members__[name]
96+
97+
# this creates the new unknown value member
98+
obj = object.__new__(cls)
99+
obj._name_ = name
100+
obj._value_ = value
101+
102+
# and adds it to the member mapping of this enum so we don't
103+
# create a different enum member value each time
104+
cls._member_map_[name] = obj
105+
cls._value2member_map_[value] = obj
106+
return obj
198107

199108

200109
class ChannelType(Enum):

0 commit comments

Comments
 (0)