@@ -62,8 +62,15 @@ def snake_enum_to_pascal(name: str) -> str:
6262 return "" .join (word .capitalize () for word in words )
6363
6464
65+ _native_enum_cache : dict [str , type ] = {}
66+
67+
6568class NativeEnum :
66- """Generic base class that dynamically maps .NET enums to Pythonic attributes."""
69+ """Generic base class that dynamically maps .NET enums to Pythonic attributes.
70+
71+ Prefer explicit creation via `NativeEnum.from_dotnet_type(dotnet_type)` rather than relying
72+ on implicit subclass side-effects.
73+ """
6774
6875 _native_type = None
6976
@@ -72,12 +79,16 @@ def __init_subclass__(
7279 native_type : object | None = None ,
7380 ** kwargs : dict [str , bool | int | str ],
7481 ) -> None :
82+ # Keep behavior for any existing code that relies on subclassing
7583 super ().__init_subclass__ (** kwargs )
7684
7785 # If class is dynamically constructed using type()
7886 if hasattr (cls , "_native_type" ) and cls ._native_type is not None :
7987 native_type = cls ._native_type
8088
89+ if native_type is None :
90+ return
91+
8192 native_names = [
8293 str (x )
8394 for x in native_type .GetEnumNames () # pyright: ignore[reportAttributeAccessIssue]
@@ -121,6 +132,61 @@ def __eq__(self, other: "NativeEnum") -> bool:
121132 def __hash__ (self ) -> int :
122133 return hash (self ._py_value )
123134
135+ @classmethod
136+ def from_dotnet_type (cls , dotnet_type : object ) -> type :
137+ """Create (or return cached) dynamic NativeEnum subclass for given .NET enum type.
138+
139+ The returned class exposes each enum member as a class attribute (upper snake case), and
140+ provides classmethods `from_value` and `from_name` for lookup along with `names()` and `values()`.
141+ """
142+ key = str (dotnet_type )
143+ if key in _native_enum_cache :
144+ return _native_enum_cache [key ]
145+
146+ # Create subclass
147+ name = dotnet_type .Name
148+ new_cls = type (name , (cls ,), {"_native_type" : dotnet_type })
149+
150+ native_names = [str (x ) for x in dotnet_type .GetEnumNames ()]
151+ native_values = [int (x ) for x in dotnet_type .GetEnumValues ()]
152+
153+ name_to_value : dict [str , int ] = {}
154+ value_to_member : dict [int , "NativeEnum" ] = {}
155+
156+ for n , v in zip (native_names , native_values ):
157+ py_name = to_snake_case (n , upper = True )
158+ inst = new_cls (py_name , v )
159+ setattr (new_cls , py_name , inst )
160+ name_to_value [py_name ] = v
161+ value_to_member [v ] = inst
162+
163+ # Attach lookup helpers
164+ def from_value (cls2 , value : int ) -> "NativeEnum" :
165+ try :
166+ return value_to_member [int (value )]
167+ except Exception as e :
168+ raise ValueError (f"{ value } is not a valid value for { cls2 .__name__ } " ) from e
169+
170+ def from_name (cls2 , name : str ) -> "NativeEnum" :
171+ py_name = to_snake_case (name , upper = True )
172+ try :
173+ return getattr (cls2 , py_name )
174+ except Exception as e :
175+ raise ValueError (f"{ name } is not a valid name for { cls2 .__name__ } " ) from e
176+
177+ def names_fn (cls2 ) -> list [str ]:
178+ return list (name_to_value .keys ())
179+
180+ def values_fn (cls2 ) -> list [int ]:
181+ return list (value_to_member .keys ())
182+
183+ new_cls .from_value = classmethod (from_value )
184+ new_cls .from_name = classmethod (from_name )
185+ new_cls .names = classmethod (names_fn )
186+ new_cls .values = classmethod (values_fn )
187+
188+ _native_enum_cache [key ] = new_cls
189+ return new_cls
124190
125191types = get_object_types ()
126192FormatterType = types ["Formatter" ]
@@ -131,7 +197,7 @@ def __hash__(self) -> int:
131197 "FracturedJsonOptions" ,
132198]
133199for enum_name in [x .Name for x in types .values () if x .IsEnum ]:
134- enum_type = type ( enum_name , ( NativeEnum ,), { "_native_type" : types [enum_name ]} )
200+ enum_type = NativeEnum . from_dotnet_type ( types [enum_name ])
135201 globals ()[enum_name ] = enum_type
136202 __all__ .append (enum_type ) # noqa: PYI056
137203
@@ -181,8 +247,8 @@ def get(self, name: str) -> int | bool | str | NativeEnum:
181247 prop = self ._properties [name ]["prop" ]
182248 if self ._properties [name ]["is_enum" ]:
183249 native_value = prop .GetValue (self ._dotnet_instance )
184- derived_enum = type ( prop . Name , ( NativeEnum ,), { "_native_type" : prop .PropertyType } )
185- return derived_enum ( to_snake_case ( str ( native_value ), upper = True ), ( int (native_value ) ))
250+ derived_enum = NativeEnum . from_dotnet_type ( prop .PropertyType )
251+ return derived_enum . from_value ( int (native_value ))
186252
187253 return prop .GetValue (self ._dotnet_instance )
188254
0 commit comments