44
55
66__all__ = [
7- 'EnumType' , 'EnumMeta' ,
7+ 'EnumType' , 'EnumMeta' , 'EnumDict' ,
88 'Enum' , 'IntEnum' , 'StrEnum' , 'Flag' , 'IntFlag' , 'ReprEnum' ,
99 'auto' , 'unique' , 'property' , 'verify' , 'member' , 'nonmember' ,
1010 'FlagBoundary' , 'STRICT' , 'CONFORM' , 'EJECT' , 'KEEP' ,
@@ -313,45 +313,8 @@ def __set_name__(self, enum_class, member_name):
313313 ):
314314 # no other instances found, record this member in _member_names_
315315 enum_class ._member_names_ .append (member_name )
316- # if necessary, get redirect in place and then add it to _member_map_
317- found_descriptor = None
318- descriptor_type = None
319- class_type = None
320- for base in enum_class .__mro__ [1 :]:
321- attr = base .__dict__ .get (member_name )
322- if attr is not None :
323- if isinstance (attr , (property , DynamicClassAttribute )):
324- found_descriptor = attr
325- class_type = base
326- descriptor_type = 'enum'
327- break
328- elif _is_descriptor (attr ):
329- found_descriptor = attr
330- descriptor_type = descriptor_type or 'desc'
331- class_type = class_type or base
332- continue
333- else :
334- descriptor_type = 'attr'
335- class_type = base
336- if found_descriptor :
337- redirect = property ()
338- redirect .member = enum_member
339- redirect .__set_name__ (enum_class , member_name )
340- if descriptor_type in ('enum' ,'desc' ):
341- # earlier descriptor found; copy fget, fset, fdel to this one.
342- redirect .fget = getattr (found_descriptor , 'fget' , None )
343- redirect ._get = getattr (found_descriptor , '__get__' , None )
344- redirect .fset = getattr (found_descriptor , 'fset' , None )
345- redirect ._set = getattr (found_descriptor , '__set__' , None )
346- redirect .fdel = getattr (found_descriptor , 'fdel' , None )
347- redirect ._del = getattr (found_descriptor , '__delete__' , None )
348- redirect ._attr_type = descriptor_type
349- redirect ._cls_type = class_type
350- setattr (enum_class , member_name , redirect )
351- else :
352- setattr (enum_class , member_name , enum_member )
353- # now add to _member_map_ (even aliases)
354- enum_class ._member_map_ [member_name ] = enum_member
316+
317+ enum_class ._add_member_ (enum_member , member_name )
355318 try :
356319 # This may fail if value is not hashable. We can't add the value
357320 # to the map, and by-value lookups for this value will be
@@ -362,7 +325,7 @@ def __set_name__(self, enum_class, member_name):
362325 enum_class ._unhashable_values_ .append (value )
363326
364327
365- class _EnumDict (dict ):
328+ class EnumDict (dict ):
366329 """
367330 Track enum member order and ensure member names are not reused.
368331
@@ -371,7 +334,7 @@ class _EnumDict(dict):
371334 """
372335 def __init__ (self ):
373336 super ().__init__ ()
374- self ._member_names = {} # use a dict to keep insertion order
337+ self ._member_names = {} # use a dict -- faster look-up than a list, and keeps insertion order since 3.7 (?)
375338 self ._last_values = []
376339 self ._ignore = []
377340 self ._auto_called = False
@@ -468,6 +431,10 @@ def __setitem__(self, key, value):
468431 self ._last_values .append (value )
469432 super ().__setitem__ (key , value )
470433
434+ @property
435+ def member_names (self ):
436+ return list (self ._member_names )
437+
471438 def update (self , members , ** more_members ):
472439 try :
473440 for name in members .keys ():
@@ -478,6 +445,8 @@ def update(self, members, **more_members):
478445 for name , value in more_members .items ():
479446 self [name ] = value
480447
448+ _EnumDict = EnumDict # keep private name for backwards compatibility
449+
481450
482451class EnumType (type ):
483452 """
@@ -489,7 +458,7 @@ def __prepare__(metacls, cls, bases, **kwds):
489458 # check that previous enum members do not exist
490459 metacls ._check_for_existing_members_ (cls , bases )
491460 # create the namespace dict
492- enum_dict = _EnumDict ()
461+ enum_dict = EnumDict ()
493462 enum_dict ._cls_name = cls
494463 # inherit previous flags and _generate_next_value_ function
495464 member_type , first_enum = metacls ._get_mixins_ (cls , bases )
@@ -1046,7 +1015,80 @@ def _find_new_(mcls, classdict, member_type, first_enum):
10461015 else :
10471016 use_args = True
10481017 return __new__ , save_new , use_args
1049- EnumMeta = EnumType
1018+
1019+ def _add_alias_ (cls , member , name ):
1020+ if name in cls ._member_map_ :
1021+ if cls ._member_map_ [name ] is not member :
1022+ raise NameError ('%r is already bound: %r' % (name , cls ._member_map_ [name ]))
1023+ return
1024+ #
1025+ # if necessary, get redirect in place and then add it to _member_map_
1026+ found_descriptor = None
1027+ descriptor_type = None
1028+ class_type = None
1029+ for base in cls .__mro__ [1 :]:
1030+ attr = base .__dict__ .get (name )
1031+ if attr is not None :
1032+ if isinstance (attr , (property , DynamicClassAttribute )):
1033+ found_descriptor = attr
1034+ class_type = base
1035+ descriptor_type = 'enum'
1036+ break
1037+ elif _is_descriptor (attr ):
1038+ found_descriptor = attr
1039+ descriptor_type = descriptor_type or 'desc'
1040+ class_type = class_type or base
1041+ continue
1042+ else :
1043+ descriptor_type = 'attr'
1044+ class_type = base
1045+ if found_descriptor :
1046+ redirect = property ()
1047+ redirect .member = member
1048+ redirect .__set_name__ (cls , name )
1049+ if descriptor_type in ('enum' ,'desc' ):
1050+ # earlier descriptor found; copy fget, fset, fdel to this one.
1051+ redirect .fget = getattr (found_descriptor , 'fget' , None )
1052+ redirect ._get = getattr (found_descriptor , '__get__' , None )
1053+ redirect .fset = getattr (found_descriptor , 'fset' , None )
1054+ redirect ._set = getattr (found_descriptor , '__set__' , None )
1055+ redirect .fdel = getattr (found_descriptor , 'fdel' , None )
1056+ redirect ._del = getattr (found_descriptor , '__delete__' , None )
1057+ redirect ._attr_type = descriptor_type
1058+ redirect ._cls_type = class_type
1059+ setattr (cls , name , redirect )
1060+ else :
1061+ setattr (cls , name , member )
1062+ # now add to _member_map_ (even aliases)
1063+ cls ._member_map_ [name ] = member
1064+ #
1065+ cls ._member_map_ [name ] = member
1066+ _add_member_ = _add_alias_ # use _add_member_ internally
1067+
1068+ def _add_value_alias_ (cls , member , value ):
1069+ try :
1070+ if value in cls ._value2member_map_ :
1071+ if cls ._value2member_map_ [value ] is not member :
1072+ raise ValueError ('%r is already bound: %r' % (value , cls ._value2member_map_ [value ]))
1073+ return
1074+ except TypeError :
1075+ # unhashable value, do long search
1076+ for m in cls ._member_map_ .values ():
1077+ if m ._value_ == value :
1078+ if m is not member :
1079+ raise ValueError ('%r is already bound: %r' % (value , cls ._value2member_map_ [value ]))
1080+ return
1081+ try :
1082+ # This may fail if value is not hashable. We can't add the value
1083+ # to the map, and by-value lookups for this value will be
1084+ # linear.
1085+ cls ._value2member_map_ .setdefault (value , member )
1086+ except TypeError :
1087+ # keep track of the value in a list so containment checks are quick
1088+ cls ._unhashable_values_ .append (value )
1089+
1090+
1091+ EnumMeta = EnumType # keep EnumMeta name for backwards compatibility
10501092
10511093
10521094class Enum (metaclass = EnumType ):
@@ -1113,7 +1155,7 @@ def __new__(cls, value):
11131155 except TypeError :
11141156 # not there, now do long search -- O(n) behavior
11151157 for member in cls ._member_map_ .values ():
1116- if member ._value_ == value :
1158+ if member ._value_ is value or member . _value_ == value :
11171159 return member
11181160 # still not found -- verify that members exist, in-case somebody got here mistakenly
11191161 # (such as via super when trying to override __new__)
0 commit comments