1919from dataclasses import dataclass
2020from functools import cached_property
2121
22- VERSION = (2 , 2 , 5 )
22+ VERSION = (2 , 3 , 0 )
2323
2424__title__ = "Enum Properties"
2525__version__ = "." .join (str (i ) for i in VERSION )
@@ -230,6 +230,38 @@ class SymmetricMixin(with_typehint("EnumProperties")): # type: ignore
230230 property will be a case sensitive symmetric property.
231231 """
232232
233+ _ep_symmetric_map_ : t .Dict [t .Any , enum .Enum ]
234+ """
235+ The case sensitive mapping of symmetric values to enumeration values.
236+ """
237+
238+ _ep_isymmetric_map_ : t .Dict [str , enum .Enum ]
239+ """
240+ The case insensitive mapping of symmetric values to enumeration values.
241+ """
242+
243+ _ep_coerce_types_ : t .List [t .Type [t .Any ]]
244+ """
245+ On instantiation, if _missing_ is invoked a coercion attempt will be made to each
246+ of these types before failure.
247+ """
248+
249+ _num_sym_props_ : int
250+ """
251+ The number of symmetric properties on this enumeration.
252+ """
253+
254+ _properties_ : t .List [_Prop ]
255+ """
256+ List of properties defined on the enumeration class.
257+ """
258+
259+ __first_class_members__ : t .List [str ]
260+ """
261+ The list of first class members - this includes all members and aliases. May be
262+ overridden.
263+ """
264+
233265 def __eq__ (self , value : t .Any ) -> bool :
234266 """Symmetric equality - try to coerce value before failure"""
235267 if isinstance (value , self .__class__ ):
@@ -346,6 +378,7 @@ class MyEnum(SymmetricMixin, enum.Enum, metaclass=EnumPropertiesMeta):
346378 # members reserved for use by EnumProperties
347379 RESERVED = [
348380 "_properties_" ,
381+ "_num_sym_props_" ,
349382 "_ep_coerce_types_" ,
350383 "_ep_symmetric_map_" ,
351384 "_ep_isymmetric_map_" ,
@@ -354,7 +387,9 @@ class MyEnum(SymmetricMixin, enum.Enum, metaclass=EnumPropertiesMeta):
354387 _ep_symmetric_map_ : t .Dict [t .Any , enum .Enum ]
355388 _ep_isymmetric_map_ : t .Dict [str , enum .Enum ]
356389 _ep_coerce_types_ : t .List [t .Type [t .Any ]]
390+ _num_sym_props_ : int
357391 _properties_ : t .List [_Prop ]
392+ __first_class_members__ : t .List [str ]
358393
359394 @classmethod
360395 def __prepare__ (mcs , cls , bases , ** kwargs ):
@@ -396,6 +431,7 @@ class _PropertyEnumDict(class_dict.__class__): # type: ignore[name-defined]
396431 _ids_ : t .Dict [int , str ] = {}
397432 _member_names : t .Union [t .List [str ], t .Dict [str , t .Any ]]
398433 _create_properties_ : bool = False
434+ __first_class_members__ : t .List [str ] = []
399435
400436 class AnnotationPropertyRecorder (dict ):
401437 class_dict : "_PropertyEnumDict"
@@ -479,6 +515,7 @@ def __setitem__(self, key, value):
479515 # todo remove below when minimum python >= 3.13
480516 not isinstance (value , type )
481517 ):
518+ self .__first_class_members__ .append (key )
482519 try :
483520 num_vals = len (value ) - len (self ._ep_properties_ )
484521 if num_vals < 1 or len (self ._ep_properties_ ) != len (
@@ -553,10 +590,16 @@ def __new__(mcs, classname, bases, classdict, **kwargs):
553590 ** kwargs ,
554591 )
555592 cls ._ep_coerce_types_ = []
593+ cls ._num_sym_props_ = 0
556594 cls ._ep_symmetric_map_ = cls ._member_map_
557595 cls ._ep_isymmetric_map_ = {}
558596 cls ._properties_ = list (classdict ._ep_properties_ .keys ())
559597
598+ # we allow users to override this
599+ cls .__first_class_members__ = classdict .get (
600+ "__first_class_members__" , classdict .__first_class_members__
601+ )
602+
560603 if classdict ._specialized_ :
561604 for val in cls : # type: ignore[var-annotated]
562605 val = t .cast (enum .Enum , val )
@@ -598,11 +641,13 @@ def add_coerce_type(typ: t.Type[t.Any]):
598641 setattr (member , prop , values [idx ])
599642
600643 # we reverse to maintain precedence order for symmetric lookups
644+ cls ._num_sym_props_ = 0
601645 member_values = t .cast (
602646 t .List [enum .Enum ],
603647 list (cls ._value2member_map_ .values () or cls .__members__ .values ()),
604648 )
605649 for prop in reversed ([prop for prop in cls ._properties_ if prop .symmetric ]):
650+ cls ._num_sym_props_ += 1
606651 prop = t .cast (_SProp , prop )
607652 for idx , val2 in enumerate (reversed (classdict ._ep_properties_ [prop ])):
608653 enum_cls = member_values [len (member_values ) - 1 - idx ]
@@ -615,6 +660,7 @@ def add_coerce_type(typ: t.Type[t.Any]):
615660 add_coerce_type (type (val2 ))
616661
617662 # add builtin symmetries
663+ cls ._num_sym_props_ += len (getattr (cls , "_symmetric_builtins_" , []))
618664 for sym_builtin in reversed (getattr (cls , "_symmetric_builtins_" , [])):
619665 # allow simple strings for the default case
620666 if isinstance (sym_builtin , str ):
0 commit comments