diff -r e0f36a9420e4 Lib/enum.py --- a/Lib/enum.py Thu Aug 15 16:19:33 2013 -0400 +++ b/Lib/enum.py Fri Aug 16 00:13:24 2013 -0700 @@ -43,20 +43,22 @@ def _is_sunder(name): name[-2:-1] != '_') def _make_class_unpicklable(cls): """Make the given class un-picklable.""" def _break_on_call_reduce(self): raise TypeError('%r cannot be pickled' % self) cls.__reduce__ = _break_on_call_reduce cls.__module__ = '' +# used to decide which __format__ to call: value's or member's +_remove_plain_format_chars = {ord(k):None for k in '<^>0123456789'} class _EnumDict(dict): """Keeps track of definition order of the enum items. EnumMeta will use the names found in self._member_names as the enumeration member names. """ def __init__(self): super().__init__() @@ -175,21 +177,21 @@ class EnumMeta(type): 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. enum_class._value2member_map_[value] = enum_member except TypeError: pass # double check that repr and friends are not the mixin's or various # things break (such as pickle) - for name in ('__repr__', '__str__', '__getnewargs__'): + for name in ('__repr__', '__str__', '__format__', '__getnewargs__'): class_method = getattr(enum_class, name) obj_method = getattr(member_type, name, None) enum_method = getattr(first_enum, name, None) if obj_method is not None and obj_method is class_method: setattr(enum_class, name, enum_method) # replace any other __new__ with our own (as long as Enum is not None, # anyway) -- again, this is to support pickle if Enum is not None: # if the user defined their own __new__, save it before it gets @@ -434,20 +436,35 @@ class Enum(metaclass=EnumMeta): return "%s.%s" % (self.__class__.__name__, self._name_) def __dir__(self): return (['__class__', '__doc__', 'name', 'value']) def __eq__(self, other): if type(other) is self.__class__: return self is other return NotImplemented + def __format__(self, format_spec): + # pure Enum branch + if self._member_type_ is object: + obj = str + val = str(self) + # mix-in branch if non-width and/or non-just chars used in format spec + elif format_spec.translate(_remove_plain_format_chars): + obj = self._member_type_ + val = self.value + # mix-in branch if only width and/or just chars used in format spec + else: + obj = str + val = str(self) + return obj.__format__(val, format_spec) + def __getnewargs__(self): return (self._value_, ) def __hash__(self): return hash(self._name_) # _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 diff -r e0f36a9420e4 Lib/test/test_enum.py --- a/Lib/test/test_enum.py Thu Aug 15 16:19:33 2013 -0400 +++ b/Lib/test/test_enum.py Fri Aug 16 00:13:24 2013 -0700 @@ -60,20 +60,47 @@ except Exception: class TestEnum(unittest.TestCase): def setUp(self): class Season(Enum): SPRING = 1 SUMMER = 2 AUTUMN = 3 WINTER = 4 self.Season = Season + class Konstants(float, Enum): + E = 2.7182818 + PI = 3.1415926 + TAU = 2 * PI + self.Konstants = Konstants + + class Grades(IntEnum): + A = 5 + B = 4 + C = 3 + D = 2 + F = 0 + self.Grades = Grades + + class Directional(str, Enum): + EAST = 'east' + WEST = 'west' + NORTH = 'north' + SOUTH = 'south' + self.Directional = Directional + + 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__', 'SPRING', 'SUMMER', 'AUTUMN', 'WINTER']), ) def test_dir_on_item(self): Season = self.Season @@ -200,20 +227,74 @@ class TestEnum(unittest.TestCase): class Huh(Enum): name = 1 value = 2 self.assertEqual( list(Huh), [Huh.name, Huh.value], ) self.assertIs(type(Huh.name), Huh) self.assertEqual(Huh.name.name, 'name') self.assertEqual(Huh.name.value, 1) + + def test_format_enum(self): + Season = self.Season + self.assertEqual('{}'.format(Season.SPRING), '{}'.format(str(Season.SPRING))) + self.assertEqual('{:}'.format(Season.SPRING), '{:}'.format(str(Season.SPRING))) + self.assertEqual('{:20}'.format(Season.SPRING), '{:20}'.format(str(Season.SPRING))) + self.assertEqual('{:^20}'.format(Season.SPRING), '{:^20}'.format(str(Season.SPRING))) + self.assertEqual('{:>20}'.format(Season.SPRING), '{:>20}'.format(str(Season.SPRING))) + self.assertEqual('{:<20}'.format(Season.SPRING), '{:<20}'.format(str(Season.SPRING))) + + def test_format_enum_date(self): + Holiday = self.Holiday + self.assertEqual('{}'.format(Holiday.IDES_OF_MARCH), '{}'.format(str(Holiday.IDES_OF_MARCH))) + self.assertEqual('{:}'.format(Holiday.IDES_OF_MARCH), '{:}'.format(str(Holiday.IDES_OF_MARCH))) + self.assertEqual('{:20}'.format(Holiday.IDES_OF_MARCH), '{:20}'.format(str(Holiday.IDES_OF_MARCH))) + self.assertEqual('{:^20}'.format(Holiday.IDES_OF_MARCH), '{:^20}'.format(str(Holiday.IDES_OF_MARCH))) + self.assertEqual('{:>20}'.format(Holiday.IDES_OF_MARCH), '{:>20}'.format(str(Holiday.IDES_OF_MARCH))) + self.assertEqual('{:<20}'.format(Holiday.IDES_OF_MARCH), '{:<20}'.format(str(Holiday.IDES_OF_MARCH))) + self.assertEqual('{:%Y %m}'.format(Holiday.IDES_OF_MARCH), '{:%Y %m}'.format(Holiday.IDES_OF_MARCH.value)) + self.assertEqual('{:%Y %m %M:00}'.format(Holiday.IDES_OF_MARCH), '{:%Y %m %M:00}'.format(Holiday.IDES_OF_MARCH.value)) + + def test_format_enum_float(self): + Konstants = self.Konstants + self.assertEqual('{}'.format(Konstants.TAU), '{}'.format(str(Konstants.TAU))) + self.assertEqual('{:}'.format(Konstants.TAU), '{:}'.format(str(Konstants.TAU))) + self.assertEqual('{:20}'.format(Konstants.TAU), '{:20}'.format(str(Konstants.TAU))) + self.assertEqual('{:^20}'.format(Konstants.TAU), '{:^20}'.format(str(Konstants.TAU))) + self.assertEqual('{:>20}'.format(Konstants.TAU), '{:>20}'.format(str(Konstants.TAU))) + self.assertEqual('{:<20}'.format(Konstants.TAU), '{:<20}'.format(str(Konstants.TAU))) + self.assertEqual('{:n}'.format(Konstants.TAU), '{:n}'.format(Konstants.TAU.value)) + self.assertEqual('{:5.2}'.format(Konstants.TAU), '{:5.2}'.format(Konstants.TAU.value)) + self.assertEqual('{:f}'.format(Konstants.TAU), '{:f}'.format(Konstants.TAU.value)) + + def test_format_enum_int(self): + Grades = self.Grades + self.assertEqual('{}'.format(Grades.C), '{}'.format(str(Grades.C))) + self.assertEqual('{:}'.format(Grades.C), '{:}'.format(str(Grades.C))) + self.assertEqual('{:20}'.format(Grades.C), '{:20}'.format(str(Grades.C))) + self.assertEqual('{:^20}'.format(Grades.C), '{:^20}'.format(str(Grades.C))) + self.assertEqual('{:>20}'.format(Grades.C), '{:>20}'.format(str(Grades.C))) + self.assertEqual('{:<20}'.format(Grades.C), '{:<20}'.format(str(Grades.C))) + self.assertEqual('{:+}'.format(Grades.C), '{:+}'.format(Grades.C.value)) + self.assertEqual('{:08X}'.format(Grades.C), '{:08x}'.format(Grades.C.value)) + self.assertEqual('{:b}'.format(Grades.C), '{:b}'.format(Grades.C.value)) + + def test_format_enum_str(self): + Directional = self.Directional + self.assertEqual('{}'.format(Directional.WEST), '{}'.format(str(Directional.WEST))) + self.assertEqual('{:}'.format(Directional.WEST), '{:}'.format(str(Directional.WEST))) + self.assertEqual('{:20}'.format(Directional.WEST), '{:20}'.format(str(Directional.WEST))) + self.assertEqual('{:^20}'.format(Directional.WEST), '{:^20}'.format(str(Directional.WEST))) + self.assertEqual('{:>20}'.format(Directional.WEST), '{:>20}'.format(str(Directional.WEST))) + self.assertEqual('{:<20}'.format(Directional.WEST), '{:<20}'.format(str(Directional.WEST))) + def test_hash(self): Season = self.Season dates = {} dates[Season.WINTER] = '1225' dates[Season.SPRING] = '0315' dates[Season.SUMMER] = '0704' dates[Season.AUTUMN] = '1031' self.assertEqual(dates[Season.AUTUMN], '1031') def test_intenum_from_scratch(self): @@ -225,29 +306,29 @@ class TestEnum(unittest.TestCase): def test_intenum_inherited(self): class IntEnum(int, Enum): pass class phy(IntEnum): pi = 3 tau = 2 * pi self.assertTrue(phy.pi < phy.tau) def test_floatenum_from_scratch(self): class phy(float, Enum): - pi = 3.141596 + pi = 3.1415926 tau = 2 * pi self.assertTrue(phy.pi < phy.tau) def test_floatenum_inherited(self): class FloatEnum(float, Enum): pass class phy(FloatEnum): - pi = 3.141596 + pi = 3.1415926 tau = 2 * pi self.assertTrue(phy.pi < phy.tau) def test_strenum_from_scratch(self): class phy(str, Enum): pi = 'Pi' tau = 'Tau' self.assertTrue(phy.pi < phy.tau) def test_strenum_inherited(self):