diff -r e33b4c18af59 Lib/enum.py --- a/Lib/enum.py Wed Sep 16 12:18:55 2015 -0400 +++ b/Lib/enum.py Thu Sep 17 00:22:52 2015 -0700 @@ -1,12 +1,11 @@ import sys -from collections import OrderedDict from types import MappingProxyType, DynamicClassAttribute __all__ = ['Enum', 'IntEnum', 'unique'] def _is_descriptor(obj): """Returns True if obj is a descriptor, False otherwise.""" return ( hasattr(obj, '__get__') or hasattr(obj, '__set__') or @@ -106,21 +105,22 @@ class EnumMeta(type): raise ValueError('Invalid enum member name: {0}'.format( ','.join(invalid_names))) # create a default docstring if one has not been provided if '__doc__' not in classdict: classdict['__doc__'] = 'An enumeration.' # create our new Enum type enum_class = super().__new__(metacls, cls, bases, classdict) enum_class._member_names_ = [] # names in definition order - enum_class._member_map_ = OrderedDict() # name->value map + enum_class._all_member_names_ = [] # including aliases + enum_class._member_map_ = {} # name->value map enum_class._member_type_ = member_type # save attributes from super classes so we know if we can take # the shortcut of storing members in the class dict base_attributes = {a for b in bases for a in b.__dict__} # Reverse value->name map for hashable values. enum_class._value2member_map_ = {} # If a custom type is mixed into the Enum, and it does not know how @@ -166,20 +166,22 @@ class EnumMeta(type): enum_member.__init__(*args) # If another member with the same value was already defined, the # new member becomes an alias to the existing one. for name, canonical_member in enum_class._member_map_.items(): if canonical_member._value_ == enum_member._value_: enum_member = canonical_member break else: # Aliases don't appear in member names (only in __members__). enum_class._member_names_.append(member_name) + # Aliases do appear in _all_member_names_ + enum_class._all_member_names_.append(member_name) # performance boost for any member that would not shadow # a DynamicClassAttribute if member_name not in base_attributes: setattr(enum_class, member_name, enum_member) # now add to _member_map_ enum_class._member_map_[member_name] = enum_member try: # This may fail if value is not hashable. We can't add the value # to the map, and by-value lookups for this value will be # linear. @@ -550,19 +552,20 @@ class Enum(metaclass=EnumMeta): class IntEnum(int, Enum): """Enum where members are also (and must be) ints""" def _reduce_ex_by_name(self, proto): return self.name def unique(enumeration): """Class decorator for enumerations ensuring unique member values.""" duplicates = [] - for name, member in enumeration.__members__.items(): + for name in enumeration._all_member_names_: + member = enumeration.__members__[name] if name != member.name: duplicates.append((name, member.name)) if duplicates: alias_details = ', '.join( ["%s -> %s" % (alias, name) for (alias, name) in duplicates]) raise ValueError('duplicate values found in %r: %s' % (enumeration, alias_details)) return enumeration diff -r e33b4c18af59 Lib/test/test_enum.py --- a/Lib/test/test_enum.py Wed Sep 16 12:18:55 2015 -0400 +++ b/Lib/test/test_enum.py Thu Sep 17 00:22:52 2015 -0700 @@ -312,24 +312,25 @@ class TestEnum(unittest.TestCase): lst, [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER, ]) self.assertIs(Season.FALL, Season.AUTUMN) self.assertEqual(Season.FALL.value, 3) self.assertEqual(Season.AUTUMN.value, 3) self.assertIs(Season(3), Season.AUTUMN) self.assertIs(Season(1), Season.SPRING) self.assertEqual(Season.FALL.name, 'AUTUMN') - self.assertEqual( - [k for k,v in Season.__members__.items() if v.name != k], - ['FALL', 'ANOTHER_SPRING'], - ) + dups = [ + k for k in Season._all_member_names_ + if Season.__members__[k].name != k + ] + self.assertEqual(dups, ['FALL', 'ANOTHER_SPRING']) def test_duplicate_name(self): with self.assertRaises(TypeError): class Color(Enum): red = 1 green = 2 blue = 3 red = 4 with self.assertRaises(TypeError):