summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorthatneat <thatneat@users.noreply.github.com>2019-07-04 18:28:37 (GMT)
committerEthan Furman <ethan@stoneleaf.us>2019-07-04 18:28:37 (GMT)
commit2f19e82fbe98ce86bcd98a176328af2808b678e8 (patch)
tree0febe00f3456e58aa82575a2c16a62f3e2385c6a
parentb4e68960b90627422325fdb75f463df1e4153c6e (diff)
downloadcpython-2f19e82fbe98ce86bcd98a176328af2808b678e8.zip
cpython-2f19e82fbe98ce86bcd98a176328af2808b678e8.tar.gz
cpython-2f19e82fbe98ce86bcd98a176328af2808b678e8.tar.bz2
bpo-37479: on Enum subclasses with mixins, __format__ uses overridden __str__ (GH-14545)
* bpo-37479: on Enum subclasses with mixins, __format__ uses overridden __str__
-rw-r--r--Doc/library/enum.rst8
-rw-r--r--Lib/enum.py5
-rw-r--r--Lib/test/test_enum.py53
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS.d/next/Library/2019-07-02-12-43-57.bpo-37479.O53a5S.rst2
5 files changed, 63 insertions, 6 deletions
diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
index 19277d7..d7d319a 100644
--- a/Doc/library/enum.rst
+++ b/Doc/library/enum.rst
@@ -739,9 +739,11 @@ Some rules:
:meth:`__str__` and :meth:`__repr__` respectively; other codes (such as
`%i` or `%h` for IntEnum) treat the enum member as its mixed-in type.
5. :ref:`Formatted string literals <f-strings>`, :meth:`str.format`,
- and :func:`format` will use the mixed-in
- type's :meth:`__format__`. If the :class:`Enum` class's :func:`str` or
- :func:`repr` is desired, use the `!s` or `!r` format codes.
+ and :func:`format` will use the mixed-in type's :meth:`__format__`
+ unless :meth:`__str__` or :meth:`__format__` is overridden in the subclass,
+ in which case the overridden methods or :class:`Enum` methods will be used.
+ Use the !s and !r format codes to force usage of the :class:`Enum` class's
+ :meth:`__str__` and :meth:`__repr__` methods.
When to use :meth:`__new__` vs. :meth:`__init__`
------------------------------------------------
diff --git a/Lib/enum.py b/Lib/enum.py
index 403f747..69a7f49 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -622,8 +622,9 @@ class Enum(metaclass=EnumMeta):
# we can get strange results with the Enum name showing up instead of
# the value
- # pure Enum branch
- if self._member_type_ is object:
+ # pure Enum branch, or branch with __str__ explicitly overridden
+ str_overridden = type(self).__str__ != Enum.__str__
+ if self._member_type_ is object or str_overridden:
cls = str
val = str(self)
# mix-in branch
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index 47081cf..fcfa7d9 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -445,12 +445,63 @@ class TestEnum(unittest.TestCase):
self.assertEqual('{:<20}'.format(Season.SPRING),
'{:<20}'.format(str(Season.SPRING)))
- def test_format_enum_custom(self):
+ def test_str_override_enum(self):
+ class EnumWithStrOverrides(Enum):
+ one = auto()
+ two = auto()
+
+ def __str__(self):
+ return 'Str!'
+ self.assertEqual(str(EnumWithStrOverrides.one), 'Str!')
+ self.assertEqual('{}'.format(EnumWithStrOverrides.one), 'Str!')
+
+ def test_format_override_enum(self):
+ class EnumWithFormatOverride(Enum):
+ one = 1.0
+ two = 2.0
+ def __format__(self, spec):
+ return 'Format!!'
+ self.assertEqual(str(EnumWithFormatOverride.one), 'EnumWithFormatOverride.one')
+ self.assertEqual('{}'.format(EnumWithFormatOverride.one), 'Format!!')
+
+ def test_str_and_format_override_enum(self):
+ class EnumWithStrFormatOverrides(Enum):
+ one = auto()
+ two = auto()
+ def __str__(self):
+ return 'Str!'
+ def __format__(self, spec):
+ return 'Format!'
+ self.assertEqual(str(EnumWithStrFormatOverrides.one), 'Str!')
+ self.assertEqual('{}'.format(EnumWithStrFormatOverrides.one), 'Format!')
+
+ def test_str_override_mixin(self):
+ class MixinEnumWithStrOverride(float, Enum):
+ one = 1.0
+ two = 2.0
+ def __str__(self):
+ return 'Overridden!'
+ self.assertEqual(str(MixinEnumWithStrOverride.one), 'Overridden!')
+ self.assertEqual('{}'.format(MixinEnumWithStrOverride.one), 'Overridden!')
+
+ def test_str_and_format_override_mixin(self):
+ class MixinWithStrFormatOverrides(float, Enum):
+ one = 1.0
+ two = 2.0
+ def __str__(self):
+ return 'Str!'
+ def __format__(self, spec):
+ return 'Format!'
+ self.assertEqual(str(MixinWithStrFormatOverrides.one), 'Str!')
+ self.assertEqual('{}'.format(MixinWithStrFormatOverrides.one), 'Format!')
+
+ def test_format_override_mixin(self):
class TestFloat(float, Enum):
one = 1.0
two = 2.0
def __format__(self, spec):
return 'TestFloat success!'
+ self.assertEqual(str(TestFloat.one), 'TestFloat.one')
self.assertEqual('{}'.format(TestFloat.one), 'TestFloat success!')
def assertFormatIsValue(self, spec, member):
diff --git a/Misc/ACKS b/Misc/ACKS
index a4d4372..36fe727 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -356,6 +356,7 @@ Tom Culliton
Raúl Cumplido
Antonio Cuni
Brian Curtin
+Jason Curtis
Paul Dagnelie
Lisandro Dalcin
Darren Dale
diff --git a/Misc/NEWS.d/next/Library/2019-07-02-12-43-57.bpo-37479.O53a5S.rst b/Misc/NEWS.d/next/Library/2019-07-02-12-43-57.bpo-37479.O53a5S.rst
new file mode 100644
index 0000000..cf23155
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-07-02-12-43-57.bpo-37479.O53a5S.rst
@@ -0,0 +1,2 @@
+When `Enum.__str__` is overridden in a derived class, the override will be
+used by `Enum.__format__` regardless of whether mixin classes are present. \ No newline at end of file