11# enum.py
2- # version="1.2.6 "
2+ # version="1.3.0 "
33
44
5- class EnumValue :
6- # An immutable object representing a specific enum member
7- def __init__ (self , v , n ):
8- object .__setattr__ (self , "value" , v )
9- object .__setattr__ (self , "name" , n )
5+ def _make_enum (v , n , e ):
6+ T = type (v )
107
11- def __repr__ (self ):
12- return f"{ self .name } : { self .value } "
13-
14- def __call__ (self ):
15- return self .value
16-
17- def __setattr__ (self , k , v ):
8+ def _setattr (self , k , v ):
189 raise AttributeError ("EnumValue is immutable" )
1910
20- # Helper function to extract the raw value
21- def _get_value (self , o ):
22- return o .value if isinstance (o , EnumValue ) else o
23-
24- # Arithmetic and Bitwise operations (Forward)
25- def __add__ (self , o ):
26- return self .value + self ._get_value (o )
27-
28- def __sub__ (self , o ):
29- return self .value - self ._get_value (o )
30-
31- def __mul__ (self , o ):
32- return self .value * self ._get_value (o )
33-
34- def __truediv__ (self , o ):
35- return self .value / self ._get_value (o )
36-
37- def __floordiv__ (self , o ):
38- return self .value // self ._get_value (o )
39-
40- def __mod__ (self , o ):
41- return self .value % self ._get_value (o )
42-
43- def __pow__ (self , o ):
44- return self .value ** self ._get_value (o )
45-
46- def __and__ (self , o ):
47- return self .value & self ._get_value (o )
48-
49- def __or__ (self , o ):
50- return self .value | self ._get_value (o )
51-
52- def __xor__ (self , o ):
53- return self .value ^ self ._get_value (o )
54-
55- def __lshift__ (self , o ):
56- return self .value << self ._get_value (o )
57-
58- def __rshift__ (self , o ):
59- return self .value >> self ._get_value (o )
60-
61- # Arithmetic and Bitwise operations (Reflected)
62- def __radd__ (self , o ):
63- return self ._get_value (o ) + self .value
64-
65- def __rsub__ (self , o ):
66- return self ._get_value (o ) - self .value
67-
68- def __rmul__ (self , o ):
69- return self ._get_value (o ) * self .value
70-
71- def __rtruediv__ (self , o ):
72- return self ._get_value (o ) / self .value
73-
74- def __rfloordiv__ (self , o ):
75- return self ._get_value (o ) // self .value
76-
77- def __rand__ (self , o ):
78- return self ._get_value (o ) & self .value
79-
80- def __ror__ (self , o ):
81- return self ._get_value (o ) | self .value
82-
83- def __rxor__ (self , o ):
84- return self ._get_value (o ) ^ self .value
85-
86- def __rlshift__ (self , o ):
87- return self ._get_value (o ) << self .value
88-
89- def __rrshift__ (self , o ):
90- return self ._get_value (o ) >> self .value
91-
92- # Unary operators
93- def __neg__ (self ):
94- return - self .value
95-
96- def __pos__ (self ):
97- return + self .value
98-
99- def __abs__ (self ):
100- return abs (self .value )
101-
102- def __invert__ (self ):
103- return ~ self .value
104-
105- # Comparison
106- def __eq__ (self , o ):
107- return self .value == self ._get_value (o )
108-
109- def __lt__ (self , o ):
110- return self .value < self ._get_value (o )
111-
112- def __le__ (self , o ):
113- return self .value <= self ._get_value (o )
114-
115- def __gt__ (self , o ):
116- return self .value > self ._get_value (o )
117-
118- def __ge__ (self , o ):
119- return self .value >= self ._get_value (o )
120-
121- def __ne__ (self , o ):
122- return self .value != self ._get_value (o )
11+ # Create class: type(name, bases, dict), which inherits a base type (int, str, etc.)
12+ return type ("EnumValue" , (T ,), {
13+ "name" : n ,
14+ "value" : v ,
15+ "__repr__" : lambda s : f"{ e } .{ n } : { s .value } " ,
16+ "__str__" : lambda s : f"{ e } .{ n } : { s .value } " ,
17+ "__call__" : lambda s : v ,
18+ "__setattr__" : _setattr
19+ })(v )
12320
12421
12522class Enum :
12623 def __new__ (cls , name = None , names = None ):
12724 # If a name and names are provided, create a NEW subclass of Enum
12825 if name and names :
129- # Support Functional API: Enum("Name", {"KEY ": VALUE })
26+ # Support Functional API: Enum("Name", {"KEY1 ": VALUE1, "KEY2": VALUE2, .. })
13027 # Dynamically create: class <name>
131- new_cls = type (name , (cls ,), {})
28+ new_cls = type (name , (cls ,), {"_inited" : True })
13229 for k , v in names .items ():
133- new_cls ._up (k , v )
134- new_cls ._inited = True
30+ setattr (new_cls , k , _make_enum (v , k , name ))
13531 return super ().__new__ (new_cls )
13632
13733 # Reverse lookup by value or name (e.g., Color(1) or Color("RED"))
138- if name and not names and cls is not Enum :
34+ if name and cls is not Enum :
13935 return cls ._lookup (name )
14036
14137 return super ().__new__ (cls )
14238
14339 def __init__ (self , name = None , names = None ):
14440 if "_inited" not in self .__class__ .__dict__ :
145- self ._scan ()
41+ self .list ()
14642
14743 @classmethod
14844 def _lookup (cls , v ):
149- if "_inited" not in cls .__dict__ :
150- cls ._scan ()
151-
152- # Finds an EnumValue by its raw value or name
153- for k in dir (cls ):
154- a = getattr (cls , k )
155- if isinstance (a , EnumValue ) and (a .value == v or a .name == v ):
156- return a
45+ for m in cls .list ():
46+ if m .value == v or m .name == v :
47+ return m
15748 raise AttributeError (f"{ v } is not in { cls .__name__ } " )
15849
15950 @classmethod
16051 def __iter__ (cls ):
161- if "_inited" not in cls .__dict__ :
162- cls ._scan ()
163-
164- for k in dir (cls ):
165- attr = getattr (cls , k )
166- if isinstance (attr , EnumValue ):
167- yield attr
52+ return iter (cls .list ())
16853
16954 @classmethod
17055 def list (cls ):
17156 if "_inited" not in cls .__dict__ :
172- cls ._scan ()
173-
174- # Returns a list of all members
175- return [getattr (cls , k ) for k in dir (cls ) if isinstance (getattr (cls , k ), EnumValue )]
57+ # Copy dict.items() to avoid RuntimeError when changing the dictionary
58+ for k , v in list (cls .__dict__ .items ()):
59+ if not k .startswith ("_" ) and not callable (v ):
60+ setattr (cls , k , _make_enum (v , k , cls .__name__ ))
61+ cls ._inited = True
62+ return [m for k in dir (cls ) if not k .startswith ("_" ) and hasattr (m := getattr (cls , k ), "name" )]
17663
17764 @classmethod
178- def _up (cls , k , v ):
179- setattr (cls , k , EnumValue (v , k ))
180-
181- @classmethod
182- def _scan (cls ):
183- # Convert class-level attributes (constants) to EnumValue objects
184- for k , v in list (cls .__dict__ .items ()):
185- if not k .startswith ("_" ) and not callable (v ) and not isinstance (v , EnumValue ):
186- cls ._up (k , v )
187- cls ._inited = True
188-
189- def is_value (self , v ):
190- return any (m .value == v for m in self )
65+ def is_value (cls , v ):
66+ return any (m .value == v for m in cls .list ())
19167
19268 def __repr__ (self ):
19369 # Supports the condition: obj == eval(repr(obj))
194- d = {m .name : m .value for m in self }
70+ d = {m .name : m .value for m in self . __class__ . list () }
19571 # Return a string like: Enum(name='Name', names={'KEY1': VALUE1, 'KEY2': VALUE2, ..})
19672 return f"Enum(name='{ self .__class__ .__name__ } ', names={ d } )"
19773
19874 def __call__ (self , v ):
199- if "_inited" in self .__class__ .__dict__ :
200- self ._scan ()
201-
20275 return self ._lookup (v )
20376
20477 def __setattr__ (self , k , v ):
@@ -210,7 +83,7 @@ def __delattr__(self, k):
21083 raise AttributeError ("Enum is immutable" )
21184
21285 def __len__ (self ):
213- return sum ( 1 for _ in self )
86+ return len ( self . __class__ . list () )
21487
21588 def __eq__ (self , o ):
21689 if not isinstance (o , Enum ):
@@ -226,19 +99,26 @@ class Color(Enum):
22699 GREEN = 2
227100 BLUE = 3
228101
102+ # Basic access
103+ print (f"RED: repr={ repr (Color .RED )} , type={ type (Color .RED )} , { Color (1 ).name } " )
104+ print (f"RED: name={ Color .RED .name } , value={ Color .RED .value } , str={ str (Color .RED )} , call={ Color .RED ()} " )
105+ assert Color (1 ).value == 1
106+ assert Color .BLUE .value >= Color .GREEN .value
107+
229108 print ("Color.list():" , Color .list ())
230109
231110 # Iteration
232111 print ("Members list:" , [member for member in Color ()])
233112 print ("Names list:" , [member .name for member in Color ()])
234113 print ("Values list:" , [member .value for member in Color ()])
114+ print ()
235115
236116 # Create instance
237117 c = Color ()
238118 print (f"Enum c: { c } " )
239119
240120 # Basic access
241- print (f"RED: Name ={ c .RED .name } , Value ={ c .RED .value } , EnumValue= { c .RED } , Call ={ c .RED ()} " )
121+ print (f"RED: name ={ c .RED .name } , value ={ c .RED .value } , str= { str ( c .RED ) } , call ={ c .RED ()} " )
242122
243123 # Assertions
244124 assert c .RED .name == "RED"
@@ -247,13 +127,15 @@ class Color(Enum):
247127 assert c .RED () == 1
248128
249129 # Reverse Lookup via instance call
250- print (f"c(1) lookup object: { c (1 )} , name={ c (1 ).name } , value={ c (1 ).value } " ) # RED
130+ o = c (1 )
131+ print (f"c(1) lookup object: { o } , name={ o .name } , value={ o .value } " )
251132 assert c (1 ).name == "RED"
252133 assert c (1 ).value == 1
253134 assert c (1 ) == 1
254135
255136 try :
256137 c (999 )
138+ 0 / 0
257139 except AttributeError as e :
258140 print (f"\n AttributeError: { e } : { c } \n " )
259141
@@ -282,6 +164,7 @@ class Status(Enum):
282164 # Immutability Check
283165 try :
284166 Status .RUNNING .value = 999
167+ 0 / 0
285168 except AttributeError as e :
286169 print (f"\n Immutability check: Passed (Cannot modify EnumValue): { e } \n " )
287170
@@ -294,6 +177,7 @@ class Status(Enum):
294177 # Test: Error handling for invalid lookup
295178 try :
296179 Status (999 )
180+ 0 / 0
297181 except AttributeError as e :
298182 print (f"\n AttributeError: Invalid lookup check: Caught expected error -> { e } \n " )
299183
@@ -302,8 +186,8 @@ class Status(Enum):
302186
303187 # Verify that eval(repr(obj)) restores the object
304188 c_repr = repr (c )
305- c_restored = eval (c_repr )
306189 print (f"Original: { c_repr } " )
190+ c_restored = eval (c_repr )
307191 print (f"Restored: { repr (c_restored )} " )
308192 print (f"Objects are equal: { c == c_restored } " )
309193 assert c == c_restored
@@ -314,6 +198,8 @@ class Status(Enum):
314198 print (type (state ))
315199 assert state .ON == 1
316200 assert state .ON .name == "ON"
201+ assert state .ON > 0
202+ assert state .ON .value | state .OFF .value == 3
317203
318204 # --- 1. Unique Data Types & Class Methods ---
319205 # Enums can hold more than just integers; here we use strings and add a method.
@@ -361,11 +247,13 @@ class Empty(Enum):
361247 # Ensuring the Enum structure cannot be tampered with after creation.
362248 try :
363249 api_call .NEW_METHOD = "PATCH"
250+ 0 / 0
364251 except AttributeError as e :
365252 print (f"Caught expected mutation error: { e } " )
366253
367254 try :
368255 del api_call .GET
256+ 0 / 0
369257 except AttributeError as e :
370258 print (f"Caught expected deletion error: { e } " )
371259
0 commit comments