Skip to content

Commit 735e6b0

Browse files
committed
python-stdlib\enum: version 1.2.1.
Gemini was used Signed-off-by: Ihor Nehrutsa <Ihor.Nehrutsa@gmail.com>
1 parent 559ff60 commit 735e6b0

3 files changed

Lines changed: 211 additions & 393 deletions

File tree

python-stdlib/enum/enum.py

Lines changed: 119 additions & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -1,269 +1,157 @@
1-
_Err = "no such attribute: "
1+
# enum.py
2+
# version="1.2.1"
23

34

4-
class ValueWrapper:
5-
"""Universal wrapper for accessing values via .value or calling ()"""
6-
__slots__ = ('_v', )
7-
8-
def __init__(self, v):
9-
self._v = v
10-
11-
@property
12-
def value(self):
13-
return self._v
14-
15-
def __call__(self):
16-
return self._v
5+
class EnumValue:
6+
def __init__(self, value, name):
7+
object.__setattr__(self, 'value', value)
8+
object.__setattr__(self, 'name', name)
179

1810
def __repr__(self):
19-
return repr(self._v)
20-
21-
def __str__(self):
22-
return str(self._v)
23-
24-
# Type conversion
25-
def __int__(self):
26-
return int(self._v)
27-
28-
def __float__(self):
29-
return float(self._v)
30-
31-
def __index__(self):
32-
return int(self._v)
33-
34-
def __bool__(self):
35-
return bool(self._v)
36-
37-
# Helper function to extract the raw value
38-
def _get_v(self, other):
39-
return other._v if isinstance(other, ValueWrapper) else other
40-
41-
# Arithmetic and Bitwise operations (Forward)
42-
def __add__(self, other):
43-
return self._v + self._get_v(other)
44-
45-
def __sub__(self, other):
46-
return self._v - self._get_v(other)
47-
48-
def __mul__(self, other):
49-
return self._v * self._get_v(other)
50-
51-
def __truediv__(self, other):
52-
return self._v / self._get_v(other)
53-
54-
def __floordiv__(self, other):
55-
return self._v // self._get_v(other)
56-
57-
def __mod__(self, other):
58-
return self._v % self._get_v(other)
59-
60-
def __pow__(self, other):
61-
return self._v**self._get_v(other)
62-
63-
def __and__(self, other):
64-
return self._v & self._get_v(other)
65-
66-
def __or__(self, other):
67-
return self._v | self._get_v(other)
68-
69-
def __xor__(self, other):
70-
return self._v ^ self._get_v(other)
71-
72-
def __lshift__(self, other):
73-
return self._v << self._get_v(other)
74-
75-
def __rshift__(self, other):
76-
return self._v >> self._get_v(other)
77-
78-
# Arithmetic and Bitwise operations (Reflected)
79-
def __radd__(self, other):
80-
return self._get_v(other) + self._v
81-
82-
def __rsub__(self, other):
83-
return self._get_v(other) - self._v
84-
85-
def __rmul__(self, other):
86-
return self._get_v(other) * self._v
87-
88-
def __rtruediv__(self, other):
89-
return self._get_v(other) / self._v
90-
91-
def __rfloordiv__(self, other):
92-
return self._get_v(other) // self._v
93-
94-
def __rand__(self, other):
95-
return self._get_v(other) & self._v
96-
97-
def __ror__(self, other):
98-
return self._get_v(other) | self._v
11+
return str(self.value)
9912

100-
def __rxor__(self, other):
101-
return self._get_v(other) ^ self._v
102-
103-
def __rlshift__(self, other):
104-
return self._get_v(other) << self._v
105-
106-
def __rrshift__(self, other):
107-
return self._get_v(other) >> self._v
108-
109-
# Unary operators
110-
def __neg__(self):
111-
return -self._v
112-
113-
def __pos__(self):
114-
return +self._v
115-
116-
def __abs__(self):
117-
return abs(self._v)
118-
119-
def __invert__(self):
120-
return ~self._v
13+
def __call__(self):
14+
return self.value
12115

122-
# Comparison
12316
def __eq__(self, other):
124-
return self._v == self._get_v(other)
17+
return self.value == (other.value if isinstance(other, EnumValue) else other)
12518

126-
def __lt__(self, other):
127-
return self._v < self._get_v(other)
128-
129-
def __le__(self, other):
130-
return self._v <= self._get_v(other)
131-
132-
def __gt__(self, other):
133-
return self._v > self._get_v(other)
134-
135-
def __ge__(self, other):
136-
return self._v >= self._get_v(other)
137-
138-
def __ne__(self, other):
139-
return self._v != self._get_v(other)
140-
141-
142-
def enum(**kw_args): # `**kw_args` kept backwards compatible as in the Internet examples
143-
return Enum(kw_args)
19+
def __setattr__(self, key, value):
20+
raise AttributeError("EnumValue is immutable")
14421

14522

146-
class Enum(dict):
147-
def __init__(self, arg=None, **kwargs):
148-
super().__init__()
149-
# Use __dict__ directly for internal flags
150-
# to avoid cluttering the dictionary keyspace
151-
super().__setattr__('_is_loading', True)
23+
class Enum:
24+
def __new__(cls, *args, **kwargs):
25+
if len(args) > 0:
26+
raise TypeError(f"{cls.__name__}() kwargs allowed only, not {args} args")
27+
return super(Enum, cls).__new__(cls)
15228

29+
def __init__(self, **kwargs):
15330
# 1. Collect class-level attributes (constants)
15431
self._scan_class_attrs()
15532
# 2. Add arguments from the constructor
156-
if arg: self.append(arg)
157-
if kwargs: self.append(kwargs)
33+
if kwargs:
34+
self.append(**kwargs)
15835

159-
super().__setattr__('_is_loading', False)
36+
def _update(self, key, value):
37+
setattr(self.__class__, key, EnumValue(value, key))
16038

16139
def _scan_class_attrs(self):
162-
cls = self.__class__
163-
# Define attributes to skip (internal or explicitly requested)
164-
skipped = getattr(cls, '__skipped__', ())
40+
# Converts static class attributes into EnumValue objects
41+
# List of methods and internal names that should not be converted
42+
ignored = ('is_value', 'append')
16543

166-
for key in dir(cls):
167-
# Skip internal names, methods, and excluded attributes
168-
if key.startswith('_') or key in ('append', 'is_value', 'key_from_value'):
169-
continue
170-
if key in skipped:
44+
for key in dir(self.__class__):
45+
# Skip internal names and methods
46+
if key.startswith('_') or key in ignored:
17147
continue
17248

173-
val = getattr(cls, key)
174-
# Only wrap non-callable attributes (constants)
175-
if not callable(val):
176-
self[key] = ValueWrapper(val)
177-
178-
def append(self, arg=None, **kwargs):
179-
if isinstance(arg, dict):
180-
for k, v in arg.items():
181-
self[k] = ValueWrapper(v)
182-
else:
183-
self._arg = arg # for __str__()
184-
if kwargs:
185-
for k, v in kwargs.items():
186-
self[k] = ValueWrapper(v)
49+
value = getattr(self.__class__, key)
50+
# Convert only constants, not methods
51+
if not callable(value) and not isinstance(value, EnumValue):
52+
self._update(key, value)
53+
54+
def is_value(self, value):
55+
# Оптимізація: ітеруємося по self (де вже є __iter__), а не через dir()
56+
return any(member.value == value for member in self)
57+
58+
def append(self, **kwargs):
59+
forbidden = ('is_value', 'append', '_update', '_scan_class_attrs')
60+
for key, value in kwargs.items():
61+
if key in forbidden or key.startswith('_'):
62+
raise NameError(f"Cannot add enum member with reserved name: {key}")
63+
if hasattr(self.__class__, key):
64+
existing = getattr(self.__class__, key)
65+
if isinstance(existing, EnumValue):
66+
raise AttributeError(f"Enum member '{key}' already exists and is immutable")
67+
self._update(key, value)
18768
return self
18869

189-
def __getattr__(self, key):
190-
if key in self:
191-
return self[key]
192-
raise AttributeError(_Err + key)
70+
def __repr__(self):
71+
# Implementation of the principle: obj == eval(repr(obj))
72+
# Use !r to correctly represent values ​​(e.g., quotes for strings)
73+
members = [f"{k}={getattr(self.__class__, k).value!r}" for k in dir(self.__class__) if not k.startswith('_') and isinstance(getattr(self.__class__, k), EnumValue)]
74+
# Return a string like: Color(RED=1, GREEN=2, BLUE=3)
75+
return f"{type(self).__name__}({', '.join(members)})"
76+
77+
def __call__(self, value):
78+
for member in self:
79+
if member.value == value:
80+
return member
81+
raise ValueError(f"no such value: {value}")
19382

19483
def __setattr__(self, key, value):
195-
if self._is_loading or key.startswith('_'):
196-
# Record directly into memory as a regular variable
197-
super().__setattr__(key, value)
198-
else:
199-
# Handle as an Enum element (wrap in ValueWrapper)
200-
self[key] = ValueWrapper(value)
84+
if hasattr(self, key) and isinstance(getattr(self, key), EnumValue):
85+
raise AttributeError(f"Enum member '{key}' is immutable")
86+
super().__setattr__(key, value)
20187

202-
def is_value(self, value):
203-
return any(v._v == value for v in self.values())
204-
205-
def key_from_value(self, value):
206-
for k, v in self.items():
207-
if v._v == value: return f"{self.__class__.__name__}.{k}"
208-
raise ValueError(_Err + str(value))
209-
210-
def __dir__(self):
211-
# 1. Dictionary keys (your data: X1, X2, etc.)
212-
data_keys = list(self.keys())
213-
# 2. Class attributes (your methods: append, is_value, etc.)
214-
class_stuff = list(dir(self.__class__))
215-
# 3. Parent class attributes (for completeness)
216-
parent_attrs = list(dir(super()))
217-
# Combine and remove duplicates using set for clarity
218-
#return list(set(data_keys + class_stuff + parent_attrs))
219-
return list(set(data_keys + class_stuff))
88+
def __delattr__(self, key):
89+
if hasattr(self, key) and isinstance(getattr(self, key), EnumValue):
90+
raise AttributeError("Enum members cannot be deleted")
91+
super().__delattr__(key)
92+
93+
def __len__(self):
94+
return sum(1 for _ in self)
95+
96+
def __iter__(self):
97+
for key in dir(self.__class__):
98+
attr = getattr(self.__class__, key)
99+
if isinstance(attr, EnumValue):
100+
yield attr
101+
102+
103+
def enum(**kwargs): # `**kwargs` kept backwards compatible as in the Internet examples
104+
return Enum(**kwargs)
220105

221-
def __call__(self, value):
222-
if self.is_value(value):
223-
return value
224-
raise ValueError(_Err + f"{value}")
225106

107+
if __name__ == '__main__':
108+
# --- Usage Example ---
226109

227-
if __name__ == "__main__":
228-
# --- Usage Examples ---
110+
# 1. Creation via class
111+
class Color(Enum):
112+
RED = 1
113+
GREEN = 2
229114

230-
# 1. GPIO and Hardware Configuration
231-
class Pins(Enum):
232-
LED = 2
233-
BUTTON = 4
234-
__skipped__ = ('RESERVED_PIN', )
235-
RESERVED_PIN = 0
115+
# Create instance
116+
c = Color()
117+
print(f"Enum repr: {c}")
236118

237-
pins = Pins(SDA=21, SCL=22)
238-
print(f"I2C SDA Pin: {pins.SDA}")
239-
print(f"Is 21 a valid pin? {pins.is_value(21)}")
119+
# 2. Strict __init__ control check
120+
try:
121+
c_bad = Color('BLACK')
122+
except TypeError as e:
123+
print(f"\nTypeError: Strict Init Check: {e}\n")
240124

241-
# 2. Math and Logic
242-
brightness = Enum(MIN=0, STEP=25, MAX=255)
243-
print(f"Next level: {brightness.MIN + brightness.STEP // 2}")
244-
print(f"Calculation: {brightness.MIN + 2 * brightness.STEP}")
125+
# 3. Dynamic addition
126+
c.append(BLUE=3)
127+
print(f"c after append: {c}")
245128

246-
# Direct arithmetic without .value
247-
print(f"Complex math: {100 + brightness.STEP}")
129+
print('dir(c):', dir(c))
248130

249-
# 3. State Machine (Dynamic Expansion)
250-
status = Enum(IDLE=0, CONNECTING=1)
251-
status.append(CONNECTED=2, ERROR=3)
252-
status.DISCONNECTING = 4
131+
# 4. Immutability and name protection check
132+
try:
133+
c.append(append=True)
134+
except NameError as e:
135+
print(f"\nNameError: Reserved name protection: {e}\n")
253136

254-
for name, val in status.items():
255-
print(f"Status {name} has code {val}")
137+
# 5. Basic access
138+
print(f"RED: Name={c.RED.name}, Value={c.RED.value}, EnumValue={c.RED}, Call={c.RED()} ")
256139

257-
# 4. Working with different types
258-
commands = Enum(START="CMD_START", STOP="CMD_STOP", REBOOT_CODE=0xDEADBEEF, IS_ACTIVE=True)
140+
# 6. Assertions
141+
assert c.RED == 1
142+
assert c.RED.value == 1
143+
assert c.RED.name == 'RED'
259144

260-
if commands.IS_ACTIVE:
261-
print(f"Running command: {commands.START}")
145+
# 7. Reverse lookup
146+
print(f"c(1) lookup object: {c(1)}, Name={c(1).name}") # RED
147+
assert c(1).name == 'RED'
148+
assert c(1) == 1
262149

263-
# 5. Class Config and dir()
264-
class WebConfig(Enum):
265-
PORT = 80
266-
TIMEOUT = 5.0
150+
# 8. Iteration
151+
print("Values list:", [member.value for member in c])
152+
print("Names list:", [member.name for member in c])
267153

268-
config = WebConfig({'IP': '192.168.1.1'})
269-
print(f"Available keys in config: {list(config.keys())}")
154+
try:
155+
c(7)
156+
except ValueError as e:
157+
print(f"\nValueError: {c} {e}\n")

python-stdlib/enum/manifest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
metadata(version="1.1.0")
1+
metadata(version="1.2.0")
22

33
module("enum.py")

0 commit comments

Comments
 (0)