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:59:22 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:59:22 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): diff -r e0f36a9420e4 Lib/test/test_unicode.py --- a/Lib/test/test_unicode.py Thu Aug 15 16:19:33 2013 -0400 +++ b/Lib/test/test_unicode.py Fri Aug 16 00:59:22 2013 -0700 @@ -1113,23 +1113,40 @@ class UnicodeTest(string_tests.CommonTes self.assertEqual('%s' % Wrapper(), '\u1234') # issue 3382 NAN = float('nan') INF = float('inf') self.assertEqual('%f' % NAN, 'nan') self.assertEqual('%F' % NAN, 'NAN') self.assertEqual('%f' % INF, 'inf') self.assertEqual('%F' % INF, 'INF') - # PEP 393 - self.assertEqual('%.1s' % "a\xe9\u20ac", 'a') - self.assertEqual('%.2s' % "a\xe9\u20ac", 'a\xe9') + def test_formatting_with_enum(self): + # issue18738 + import enum + class Float(float, enum.Enum): + PI = 3.1415926 + class Int(enum.IntEnum): + IDES = 15 + class Str(str, enum.Enum): + ABC = 'abc' + # Testing Unicode formatting strings... + self.assertEqual("%s, %s" % (Str.ABC, Str.ABC), 'Str.ABC, Str.ABC') + self.assertEqual("%s, %s, %d, %i, %u, %f, %5.2f" % (Str.ABC, Str.ABC, Int.IDES, Int.IDES, Int.IDES, Float.PI, Float.PI), 'Str.ABC, Str.ABC, 15, 15, 15, 3.141593, 3.14') + + # formatting jobs delegated from the string implementation: + self.assertEqual('...%(foo)s...' % {'foo':Str.ABC}, '...Str.ABC...') + self.assertEqual('...%(foo)s...' % {'foo':Int.IDES}, '...Int.IDES...') + self.assertEqual('...%(foo)i...' % {'foo':Int.IDES}, '...15...') + self.assertEqual('...%(foo)d...' % {'foo':Int.IDES}, '...15...') + self.assertEqual('...%(foo)u...' % {'foo':Int.IDES,'def':Float.PI}, '...15...') + self.assertEqual('...%(foo)f...' % {'foo':Float.PI,'def':123}, '...3.141593...') @support.cpython_only def test_formatting_huge_precision(self): from _testcapi import INT_MAX format_string = "%.{}f".format(INT_MAX + 1) with self.assertRaises(ValueError): result = format_string % 2.34 def test_formatting_huge_width(self): format_string = "%{}f".format(sys.maxsize + 1) diff -r e0f36a9420e4 Objects/unicodeobject.c --- a/Objects/unicodeobject.c Thu Aug 15 16:19:33 2013 -0400 +++ b/Objects/unicodeobject.c Fri Aug 16 00:59:22 2013 -0700 @@ -866,21 +866,21 @@ resize_copy(PyObject *unicode, Py_ssize_ relies on that. XXX This allocator could further be enhanced by assuring that the free list never reduces its size below 1. */ static PyUnicodeObject * _PyUnicode_New(Py_ssize_t length) { - PyUnicodeObject *unicode; + register PyUnicodeObject *unicode; size_t new_size; /* Optimization for empty strings */ if (length == 0 && unicode_empty != NULL) { Py_INCREF(unicode_empty); return (PyUnicodeObject*)unicode_empty; } /* Ensure we won't overflow the size. */ if (length > ((PY_SSIZE_T_MAX / sizeof(Py_UNICODE)) - 1)) { @@ -1550,21 +1550,21 @@ int _PyUnicode_STATE(unicode).kind = PyUnicode_4BYTE_KIND; #endif PyUnicode_4BYTE_DATA(unicode)[_PyUnicode_LENGTH(unicode)] = '\0'; } _PyUnicode_STATE(unicode).ready = 1; assert(_PyUnicode_CheckConsistency(unicode, 1)); return 0; } static void -unicode_dealloc(PyObject *unicode) +unicode_dealloc(register PyObject *unicode) { switch (PyUnicode_CHECK_INTERNED(unicode)) { case SSTATE_NOT_INTERNED: break; case SSTATE_INTERNED_MORTAL: /* revive dead object temporarily for DelItem */ Py_REFCNT(unicode) = 3; if (PyDict_DelItem(interned, unicode) != 0) Py_FatalError( @@ -2280,21 +2280,21 @@ PyUnicode_AsUCS4(PyObject *string, Py_UC Py_UCS4* PyUnicode_AsUCS4Copy(PyObject *string) { return as_ucs4(string, NULL, 0, 1); } #ifdef HAVE_WCHAR_H PyObject * -PyUnicode_FromWideChar(const wchar_t *w, Py_ssize_t size) +PyUnicode_FromWideChar(register const wchar_t *w, Py_ssize_t size) { if (w == NULL) { if (size == 0) _Py_RETURN_UNICODE_EMPTY(); PyErr_BadInternalCall(); return NULL; } if (size == -1) { size = wcslen(w); @@ -2891,21 +2891,21 @@ PyUnicode_FromOrdinal(int ordinal) if (v == NULL) return NULL; kind = PyUnicode_KIND(v); data = PyUnicode_DATA(v); PyUnicode_WRITE(kind, data, 0, ordinal); assert(_PyUnicode_CheckConsistency(v, 1)); return v; } PyObject * -PyUnicode_FromObject(PyObject *obj) +PyUnicode_FromObject(register PyObject *obj) { /* XXX Perhaps we should make this API an alias of PyObject_Str() instead ?! */ if (PyUnicode_CheckExact(obj)) { if (PyUnicode_READY(obj) == -1) return NULL; Py_INCREF(obj); return obj; } if (PyUnicode_Check(obj)) { @@ -2913,21 +2913,21 @@ PyUnicode_FromObject(PyObject *obj) return a true Unicode object with the same data. */ return _PyUnicode_Copy(obj); } PyErr_Format(PyExc_TypeError, "Can't convert '%.100s' object to str implicitly", Py_TYPE(obj)->tp_name); return NULL; } PyObject * -PyUnicode_FromEncodedObject(PyObject *obj, +PyUnicode_FromEncodedObject(register PyObject *obj, const char *encoding, const char *errors) { Py_buffer buffer; PyObject *v; if (obj == NULL) { PyErr_BadInternalCall(); return NULL; } @@ -4646,23 +4646,23 @@ ascii_decode(const char *start, const ch * long are only aligned at 2-byte boundaries. Therefore the assert() * won't work; also, tests have shown that skipping the "optimised * version" will even speed up m68k. */ #if !defined(__m68k__) #if SIZEOF_LONG <= SIZEOF_VOID_P assert(_Py_IS_ALIGNED(dest, SIZEOF_LONG)); if (_Py_IS_ALIGNED(p, SIZEOF_LONG)) { /* Fast path, see in STRINGLIB(utf8_decode) for an explanation. */ - /* Help allocation */ - const char *_p = p; - Py_UCS1 * q = dest; + /* Help register allocation */ + register const char *_p = p; + register Py_UCS1 * q = dest; while (_p < aligned_end) { unsigned long value = *(const unsigned long *) _p; if (value & ASCII_CHAR_MASK) break; *((unsigned long *)q) = value; _p += SIZEOF_LONG; q += SIZEOF_LONG; } p = _p; while (p < end) { @@ -4671,22 +4671,22 @@ ascii_decode(const char *start, const ch *q++ = *p++; } return p - start; } #endif #endif while (p < end) { /* Fast path, see in STRINGLIB(utf8_decode) in stringlib/codecs.h for an explanation. */ if (_Py_IS_ALIGNED(p, SIZEOF_LONG)) { - /* Help allocation */ - const char *_p = p; + /* Help register allocation */ + register const char *_p = p; while (_p < aligned_end) { unsigned long value = *(unsigned long *) _p; if (value & ASCII_CHAR_MASK) break; _p += SIZEOF_LONG; } p = _p; if (_p == end) break; } @@ -6506,21 +6506,21 @@ PyUnicode_DecodeASCII(const char *s, e = s + size; data = writer.data; outpos = ascii_decode(s, e, (Py_UCS1 *)data); writer.pos = outpos; if (writer.pos == size) return _PyUnicodeWriter_Finish(&writer); s += writer.pos; kind = writer.kind; while (s < e) { - unsigned char c = (unsigned char)*s; + register unsigned char c = (unsigned char)*s; if (c < 128) { PyUnicode_WRITE(kind, data, writer.pos, c); writer.pos++; ++s; } else { startinpos = s-starts; endinpos = startinpos + 1; if (unicode_decode_call_errorhandler_writer( errors, &errorHandler, @@ -13560,24 +13560,25 @@ formatlong(PyObject *val, struct unicode assert(PyLong_Check(val)); switch (type) { default: assert(!"'type' not in [diuoxX]"); case 'd': case 'i': case 'u': /* Special-case boolean: we want 0/1 */ - if (PyBool_Check(val)) + /* On second thought, just use PyNumber_ToBase */ + /* if (PyBool_Check(val)) */ result = PyNumber_ToBase(val, 10); - else - result = Py_TYPE(val)->tp_str(val); + /* else + result = Py_TYPE(val)->tp_str(val); */ break; case 'o': numnondigits = 2; result = PyNumber_ToBase(val, 8); break; case 'x': case 'X': numnondigits = 2; result = PyNumber_ToBase(val, 16); break; @@ -14614,21 +14615,21 @@ void for (i = 0; i < 256; i++) Py_CLEAR(unicode_latin1[i]); _PyUnicode_ClearStaticStrings(); (void)PyUnicode_ClearFreeList(); } void PyUnicode_InternInPlace(PyObject **p) { - PyObject *s = *p; + register PyObject *s = *p; PyObject *t; #ifdef Py_DEBUG assert(s != NULL); assert(_PyUnicode_CHECK(s)); #else if (s == NULL || !PyUnicode_Check(s)) return; #endif /* If it's a subclass, we don't really know what putting it in the interned dict might do. */ @@ -14947,21 +14948,21 @@ Py_UNICODE_strcmp(const Py_UNICODE *s1, if (*s1) return 1; if (*s2) return -1; return 0; } int Py_UNICODE_strncmp(const Py_UNICODE *s1, const Py_UNICODE *s2, size_t n) { - Py_UNICODE u1, u2; + register Py_UNICODE u1, u2; for (; n != 0; n--) { u1 = *s1; u2 = *s2; if (u1 != u2) return (u1 < u2) ? -1 : +1; if (u1 == '\0') return 0; s1++; s2++; }