diff -r b0517bb271ad Lib/enum.py --- a/Lib/enum.py Sat Sep 14 18:53:26 2013 -0700 +++ b/Lib/enum.py Sat Sep 14 19:21:25 2013 -0700 @@ -9,20 +9,22 @@ class _RouteClassAttributeToGetattr: """Route attribute access on a class to __getattr__. This is a descriptor, used to define attributes that act differently when accessed through an instance and through a class. Instance access remains normal, but access to an attribute through a class will be routed to the class's __getattr__ method; this is done by raising AttributeError. """ def __init__(self, fget=None): self.fget = fget + if fget.__doc__ is not None: + self.__doc__ = fget.__doc__ def __get__(self, instance, ownerclass=None): if instance is None: raise AttributeError() return self.fget(instance) def __set__(self, instance, value): raise AttributeError("can't set attribute") def __delete__(self, instance): @@ -153,20 +155,21 @@ class EnumMeta(type): if not use_args: enum_member = __new__(enum_class) if not hasattr(enum_member, '_value_'): enum_member._value_ = value else: enum_member = __new__(enum_class, *args) if not hasattr(enum_member, '_value_'): enum_member._value_ = member_type(*args) value = enum_member._value_ enum_member._name_ = member_name + enum_member.__objclass__ = enum_class 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) @@ -216,21 +219,21 @@ class EnumMeta(type): """ if names is None: # simple value lookup return cls.__new__(cls, value) # otherwise, functional API: we're creating a new Enum type return cls._create_(value, names, module=module, type=type) def __contains__(cls, member): return isinstance(member, cls) and member.name in cls._member_map_ def __dir__(self): - return ['__class__', '__doc__', '__members__'] + self._member_names_ + return ['__class__', '__doc__', '__members__', '__module__'] + self._member_names_ def __getattr__(cls, name): """Return the enum member matching `name` We use __getattr__ instead of descriptors or inserting into the enum class' __dict__ in order to support `name` and `value` being both properties for enum members (which live in the class' __dict__) and enum members themselves. """ @@ -442,21 +445,22 @@ class Enum(metaclass=EnumMeta): raise ValueError("%s is not a valid %s" % (value, cls.__name__)) def __repr__(self): return "<%s.%s: %r>" % ( self.__class__.__name__, self._name_, self._value_) def __str__(self): return "%s.%s" % (self.__class__.__name__, self._name_) def __dir__(self): - return (['__class__', '__doc__', 'name', 'value']) + added_behavior = [m for m in self.__class__.__dict__ if m[0] != '_'] + return ['__class__', '__doc__', 'name', 'value'] + added_behavior def __eq__(self, other): if type(other) is self.__class__: return self is other return NotImplemented def __format__(self, format_spec): # mixed-in Enums should use the mixed-in type's __format__, otherwise # we can get strange results with the Enum name showing up instead of # the value @@ -479,24 +483,26 @@ class Enum(metaclass=EnumMeta): # _RouteClassAttributeToGetattr is used to provide access to the `name` # and `value` properties of enum members while keeping some measure of # protection from modification, while still allowing for an enumeration # to have members named `name` and `value`. This works because enumeration # members are not set directly on the enum class -- __getattr__ is # used to look them up. @_RouteClassAttributeToGetattr def name(self): + """The name of the Enum member.""" return self._name_ @_RouteClassAttributeToGetattr def value(self): + """The value of the Enum member.""" return self._value_ class IntEnum(int, Enum): """Enum where members are also (and must be) ints""" def unique(enumeration): """Class decorator for enumerations ensuring unique member values.""" duplicates = [] diff -r b0517bb271ad Lib/test/test_enum.py --- a/Lib/test/test_enum.py Sat Sep 14 18:53:26 2013 -0700 +++ b/Lib/test/test_enum.py Sat Sep 14 19:21:25 2013 -0700 @@ -91,31 +91,46 @@ class TestEnum(unittest.TestCase): from datetime import date class Holiday(date, Enum): NEW_YEAR = 2013, 1, 1 IDES_OF_MARCH = 2013, 3, 15 self.Holiday = Holiday def test_dir_on_class(self): Season = self.Season self.assertEqual( set(dir(Season)), - set(['__class__', '__doc__', '__members__', + set(['__class__', '__doc__', '__members__', '__module__', 'SPRING', 'SUMMER', 'AUTUMN', 'WINTER']), ) def test_dir_on_item(self): Season = self.Season self.assertEqual( set(dir(Season.WINTER)), set(['__class__', '__doc__', 'name', 'value']), ) + def test_dir_with_added_behavior(self): + class Test(Enum): + this = 'that' + these = 'those' + def wowser(self): + return ("Wowser! I'm %s!" % self.name) + self.assertEqual( + set(dir(Test)), + set(['__class__', '__doc__', '__members__', '__module__', 'this', 'these']), + ) + self.assertEqual( + set(dir(Test.this)), + set(['__class__', '__doc__', 'name', 'value', 'wowser']), + ) + def test_enum_in_enum_out(self): Season = self.Season self.assertIs(Season(Season.WINTER), Season.WINTER) def test_enum_value(self): Season = self.Season self.assertEqual(Season.SPRING.value, 1) def test_intenum_value(self): self.assertEqual(IntStooges.CURLY.value, 2)