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"\n TypeError: 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"\n NameError: 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"\n ValueError: { c } { e } \n " )
0 commit comments