|
1 |
| -from __future__ import annotations |
| 1 | +from enum import IntEnum |
2 | 2 |
|
3 |
| -from enum import ( |
4 |
| - EnumMeta, |
5 |
| - IntEnum, |
6 |
| -) |
7 |
| -from types import MappingProxyType |
8 |
| -from typing import ( |
9 |
| - TYPE_CHECKING, |
10 |
| - Any, |
11 |
| -) |
| 3 | +from typing_extensions import Self |
12 | 4 |
|
13 |
| -if TYPE_CHECKING: |
14 |
| - from collections.abc import ( |
15 |
| - Generator, |
16 |
| - Mapping, |
17 |
| - ) |
18 |
| - |
19 |
| - from typing_extensions import ( |
20 |
| - Never, |
21 |
| - Self, |
22 |
| - ) |
23 |
| - |
24 |
| - |
25 |
| -def _is_descriptor(obj: object) -> bool: |
26 |
| - return hasattr(obj, "__get__") or hasattr(obj, "__set__") or hasattr(obj, "__delete__") |
27 |
| - |
28 |
| - |
29 |
| -class EnumType(EnumMeta if TYPE_CHECKING else type): |
30 |
| - _value_map_: Mapping[int, Enum] |
31 |
| - _member_map_: Mapping[str, Enum] |
32 |
| - |
33 |
| - def __new__(mcs, name: str, bases: tuple[type, ...], namespace: dict[str, Any]) -> Self: |
34 |
| - value_map = {} |
35 |
| - member_map = {} |
36 |
| - |
37 |
| - new_mcs = type( |
38 |
| - f"{name}Type", |
39 |
| - tuple( |
40 |
| - dict.fromkeys([base.__class__ for base in bases if base.__class__ is not type] + [EnumType, type]) |
41 |
| - ), # reorder the bases so EnumType and type are last to avoid conflicts |
42 |
| - {"_value_map_": value_map, "_member_map_": member_map}, |
43 |
| - ) |
44 |
| - |
45 |
| - members = { |
46 |
| - name: value for name, value in namespace.items() if not _is_descriptor(value) and not name.startswith("__") |
47 |
| - } |
48 |
| - |
49 |
| - cls = type.__new__( |
50 |
| - new_mcs, |
51 |
| - name, |
52 |
| - bases, |
53 |
| - {key: value for key, value in namespace.items() if key not in members}, |
54 |
| - ) |
55 |
| - # this allows us to disallow member access from other members as |
56 |
| - # members become proper class variables |
57 |
| - |
58 |
| - for name, value in members.items(): |
59 |
| - member = value_map.get(value) |
60 |
| - if member is None: |
61 |
| - member = cls.__new__(cls, name=name, value=value) # type: ignore |
62 |
| - value_map[value] = member |
63 |
| - member_map[name] = member |
64 |
| - type.__setattr__(new_mcs, name, member) |
65 |
| - |
66 |
| - return cls |
67 |
| - |
68 |
| - if not TYPE_CHECKING: |
69 |
| - |
70 |
| - def __call__(cls, value: int) -> Enum: |
71 |
| - try: |
72 |
| - return cls._value_map_[value] |
73 |
| - except (KeyError, TypeError): |
74 |
| - raise ValueError(f"{value!r} is not a valid {cls.__name__}") from None |
75 |
| - |
76 |
| - def __iter__(cls) -> Generator[Enum, None, None]: |
77 |
| - yield from cls._member_map_.values() |
78 |
| - |
79 |
| - def __reversed__(cls) -> Generator[Enum, None, None]: |
80 |
| - yield from reversed(cls._member_map_.values()) |
81 |
| - |
82 |
| - def __getitem__(cls, key: str) -> Enum: |
83 |
| - return cls._member_map_[key] |
84 |
| - |
85 |
| - @property |
86 |
| - def __members__(cls) -> MappingProxyType[str, Enum]: |
87 |
| - return MappingProxyType(cls._member_map_) |
88 |
| - |
89 |
| - def __repr__(cls) -> str: |
90 |
| - return f"<enum {cls.__name__!r}>" |
91 |
| - |
92 |
| - def __len__(cls) -> int: |
93 |
| - return len(cls._member_map_) |
94 |
| - |
95 |
| - def __setattr__(cls, name: str, value: Any) -> Never: |
96 |
| - raise AttributeError(f"{cls.__name__}: cannot reassign Enum members.") |
97 |
| - |
98 |
| - def __delattr__(cls, name: str) -> Never: |
99 |
| - raise AttributeError(f"{cls.__name__}: cannot delete Enum members.") |
100 |
| - |
101 |
| - def __contains__(cls, member: object) -> bool: |
102 |
| - return isinstance(member, cls) and member.name in cls._member_map_ |
103 |
| - |
104 |
| - |
105 |
| -class Enum(IntEnum if TYPE_CHECKING else int, metaclass=EnumType): |
106 |
| - """ |
107 |
| - The base class for protobuf enumerations, all generated enumerations will |
108 |
| - inherit from this. Emulates `enum.IntEnum`. |
109 |
| - """ |
110 |
| - |
111 |
| - name: str | None |
112 |
| - value: int |
113 |
| - |
114 |
| - if not TYPE_CHECKING: |
115 |
| - |
116 |
| - def __new__(cls, *, name: str | None, value: int) -> Self: |
117 |
| - self = super().__new__(cls, value) |
118 |
| - super().__setattr__(self, "name", name) |
119 |
| - super().__setattr__(self, "value", value) |
120 |
| - return self |
121 |
| - |
122 |
| - def __str__(self) -> str: |
123 |
| - return self.name or "None" |
124 |
| - |
125 |
| - def __repr__(self) -> str: |
126 |
| - return f"{self.__class__.__name__}.{self.name}" |
127 |
| - |
128 |
| - def __setattr__(self, key: str, value: Any) -> Never: |
129 |
| - raise AttributeError(f"{self.__class__.__name__} Cannot reassign a member's attributes.") |
130 |
| - |
131 |
| - def __delattr__(self, item: Any) -> Never: |
132 |
| - raise AttributeError(f"{self.__class__.__name__} Cannot delete a member's attributes.") |
133 |
| - |
134 |
| - def __copy__(self) -> Self: |
135 |
| - return self |
136 |
| - |
137 |
| - def __deepcopy__(self, memo: Any) -> Self: |
138 |
| - return self |
139 | 5 |
|
| 6 | +class Enum(IntEnum): |
140 | 7 | @classmethod
|
141 |
| - def try_value(cls, value: int = 0) -> Self: |
142 |
| - """Return the value which corresponds to the value. |
143 |
| -
|
144 |
| - Parameters |
145 |
| - ----------- |
146 |
| - value: :class:`int` |
147 |
| - The value of the enum member to get. |
| 8 | + def _missing_(cls, value): |
| 9 | + # If the given value is not an integer, let the standard enum implementation raise an error |
| 10 | + if not isinstance(value, int): |
| 11 | + return None |
| 12 | + |
| 13 | + # Create a new "unknown" instance with the given value. |
| 14 | + obj = int.__new__(cls, value) |
| 15 | + obj._value_ = value |
| 16 | + obj._name_ = "" |
| 17 | + return obj |
| 18 | + |
| 19 | + def __str__(self): |
| 20 | + if not self.name: |
| 21 | + return f"{self.__class__.__name__}.~UNKNOWN({self.value})" |
| 22 | + return f"{self.__class__.__name__}.{self.name}" |
148 | 23 |
|
149 |
| - Returns |
150 |
| - ------- |
151 |
| - :class:`Enum` |
152 |
| - The corresponding member or a new instance of the enum if |
153 |
| - ``value`` isn't actually a member. |
154 |
| - """ |
155 |
| - try: |
156 |
| - return cls._value_map_[value] |
157 |
| - except (KeyError, TypeError): |
158 |
| - return cls.__new__(cls, name=None, value=value) |
| 24 | + def __repr__(self): |
| 25 | + if not self.name: |
| 26 | + return f"<{self.__class__.__name__}.~UNKNOWN: {self.value}>" |
| 27 | + return super().__repr__() |
159 | 28 |
|
160 | 29 | @classmethod
|
161 | 30 | def from_string(cls, name: str) -> Self:
|
162 | 31 | """Return the value which corresponds to the string name.
|
163 | 32 |
|
164 |
| - Parameters |
165 |
| - ----------- |
166 |
| - name: :class:`str` |
167 |
| - The name of the enum member to get. |
| 33 | + Parameters: |
| 34 | + name: The name of the enum member to get. |
| 35 | +
|
| 36 | + Raises: |
| 37 | + ValueError: The member was not found in the Enum. |
168 | 38 |
|
169 |
| - Raises |
170 |
| - ------- |
171 |
| - :exc:`ValueError` |
172 |
| - The member was not found in the Enum. |
| 39 | + Returns: |
| 40 | + The corresponding value |
173 | 41 | """
|
174 | 42 | try:
|
175 |
| - return cls._member_map_[name] |
| 43 | + return cls[name] |
176 | 44 | except KeyError as e:
|
177 | 45 | raise ValueError(f"Unknown value {name} for enum {cls.__name__}") from e
|
0 commit comments