]> granicus.if.org Git - python/commitdiff
bpo-37479: on Enum subclasses with mixins, __format__ uses overridden __str__ (GH...
authorthatneat <thatneat@users.noreply.github.com>
Thu, 4 Jul 2019 18:28:37 +0000 (11:28 -0700)
committerEthan Furman <ethan@stoneleaf.us>
Thu, 4 Jul 2019 18:28:37 +0000 (11:28 -0700)
* bpo-37479: on Enum subclasses with mixins, __format__ uses overridden __str__

Doc/library/enum.rst
Lib/enum.py
Lib/test/test_enum.py
Misc/ACKS
Misc/NEWS.d/next/Library/2019-07-02-12-43-57.bpo-37479.O53a5S.rst [new file with mode: 0644]

index 19277d76995fedd5a3bcd9fa5cbb7a9cf76bf974..d7d319a951345066e45a01d6c33da768a7455caf 100644 (file)
@@ -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__`
 ------------------------------------------------
index 403f747d22a4ea04605506151f1d5560a97d0aee..69a7f49d8072a46a42771c7998ea1ee59a045a15 100644 (file)
@@ -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
index 47081cf75ca086d5715be6cd8bcff9e8f0b6f745..fcfa7d923e75604c13bb5818382ee497fd938644 100644 (file)
@@ -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):
index a4d4372d3443e521402d4f5b6473a13dcca42adb..36fe727c8421c06842b5e987ce95dfaa46699842 100644 (file)
--- 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 (file)
index 0000000..cf23155
--- /dev/null
@@ -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