8000 bpo-37479: on Enum subclasses with mixins, __format__ uses overridden… · python/cpython@2f19e82 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2f19e82

Browse files
thatneatethanfurman
authored andcommitted
< 8000 div> bpo-37479: on Enum subclasses with mixins, __format__ uses overridden __str__ (GH-14545)
* bpo-37479: on Enum subclasses with mixins, __format__ uses overridden __str__
1 parent b4e6896 commit 2f19e82

File tree

5 files changed

+63
-6
lines changed

5 files changed

+63
-6
lines changed

Doc/library/enum.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -739,9 +739,11 @@ Some rules:
739739
:meth:`__str__` and :meth:`__repr__` respectively; other codes (such as
740740
`%i` or `%h` for IntEnum) treat the enum member as its mixed-in type.
741741
5. :ref:`Formatted string literals <f-strings>`, :meth:`str.format`,
742-
and :func:`format` will use the mixed-in
743-
type's :meth:`__format__`. If the :class:`Enum` class's :func:`str` or
744-
:func:`repr` is desired, use the `!s` or `!r` format codes.
742+
and :func:`format` will use the mixed-in type's :meth:`__format__`
743+
unless :meth:`__str__` or :meth:`__format__` is overridden in the subclass,
744+
in which case the overridden methods or :class:`Enum` methods will be used.
745+
Use the !s and !r format codes to force usage of the :class:`Enum` class's
746+
:meth:`__str__` and :meth:`__repr__` methods.
745747

746748
When to use :meth:`__new__` vs. :meth:`__init__`
747749
------------------------------------------------

Lib/enum.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -622,8 +622,9 @@ def __format__(self, format_spec):
622622
# we can get strange results with the Enum name showing up instead of
623623
# the value
624624

625-
# pure Enum branch
626-
if self._member_type_ is object:
625+
# pure Enum branch, or branch with __str__ explicitly overridden
626+
str_overridden = type(self).__str__ != Enum.__str__
627+
if self._member_type_ is object or str_overridden:
627628
cls = str
628629
val = str(self)
629630
# mix-in branch

Lib/test/test_enum.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,12 +445,63 @@ def test_format_enum(self):
445445
self.assertEqual('{:<20}'.format(Season.SPRING),
446446
'{:<20}'.format(str(Season.SPRING)))
447447

448-
def test_format_enum_custom(self):
448+
def test_str_override_enum(self):
449+
class EnumWithStrOverrides(Enum):
450+
one = auto()
451+
two = auto()
452+
453+
def __str__(self):
454+
return 'Str!'
455+
self.assertEqual(str(EnumWithStrOverrides.one), 'Str!')
456+
self.assertEqual('{}'.format(EnumWithStrOverrides.one), 'Str!')
457+
458+
def test_format_override_enum(self):
459+
class EnumWithFormatOverride(Enum):
460+
one = 1.0
461+
two = 2.0
462+
def __format__(self, spec):
463+
return 'Format!!'
464+
self.assertEqual(str(EnumWithFormatOverride.one), 'EnumWithFormatOverride.one')
465+
self.assertEqual('{}'.format(EnumWithFormatOverride.one), 'Format!!')
466+
467+
def test_str_and_format_override_enum(self):
468+
class EnumWithStrFormatOverrides(Enum):
469+
one = auto()
470+
two = auto()
471+
def __str__(self):
472+
return 'Str!'
473+
def __format__(self, spec):
474+
return 'Format!'
475+
self.assertEqual(str(EnumWithStrFormatOverrides.one), 'Str!')
476+
self.assertEqual('{}'.format(EnumWithStrFormatOverrides.one), 'Format!')
477+
478+
def test_str_override_mixin(self):
479+
class MixinEnumWithStrOverride(float, Enum):
480+
one = 1.0
481+
two = 2.0
482+
def __str__(self):
483+
return 'Overridden!'
484+
self.assertEqual(str(MixinEnumWithStrOverride.one), 'Overridden!')
485+
self.assertEqual('{}'.format(MixinEnumWithStrOverride.one), 'Overridden!')
486+
487+
def test_str_and_format_override_mixin(self):
488+
class MixinWithStrFormatOverrides(float, Enum):
489+
one = 1.0
490+
two = 2.0
491+
def __str__(self):
492+
return 'Str!'
493+
def __format__(self, spec):
494+
return 'Format!'
495+
self.assertEqual(str(MixinWithStrFormatOverrides.one), 'Str!')
496+
self.assertEqual('{}'.format(MixinWithStrFormatOverrides.one), 'Format!')
497+
498+
def test_format_override_mixin(self):
449499
class TestFloat(float, Enum):
450500
one = 1.0
451501
two = 2.0
452502
def __format__(self, spec):
453503
return 'TestFloat success!'
504+
self.assertEqual(str(TestFloat.one), 'TestFloat.one')
454505
self.assertEqual('{}'.format(TestFloat.one), 'TestFloat success!')
455506

456507
def assertFormatIsValue(self, spec, member):

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ Tom Culliton
356356
Raúl Cumplido
357357
Antonio Cuni
358358
Brian Curtin
359+
Jason Curtis
359360
Paul Dagnelie
360361
Lisandro Dalcin
361362
Darren Dale
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
When `Enum.__str__` is overridden in a derived class, the override will be
2+
used by `Enum.__format__` regardless of whether mixin classes are present.

0 commit comments

Comments
 (0)
0