]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.8] bpo-37479: Enum - use correct __format__ (GH-14545)
authorEthan Furman <ethan@stoneleaf.us>
Sun, 13 Sep 2020 20:47:43 +0000 (13:47 -0700)
committerGitHub <noreply@github.com>
Sun, 13 Sep 2020 20:47:43 +0000 (13:47 -0700)
* bpo-37479: on Enum subclasses with mixins, __format__ uses overridden __str__.
(cherry picked from commit 2f19e82fbe98ce86bcd98a176328af2808b678e8)

Co-authored-by: thatneat <thatneat@users.noreply.github.com>
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 ff1510cdd0e415fd115c8a4dfa293084cc7bf6cd..03aa2f1b21655f796a6be6b040620519eeef6eb5 100644 (file)
@@ -744,9 +744,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 14cc00e783915294e2a9e3a15b102ea5a8cb080c..16033b1c6fac5cab49284755700487ecbd37fba7 100644 (file)
@@ -633,8 +633,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 a2a3c567349011fe66c864726c6dabd811bfc2c0..0f91f00d92dd9c23f28a123bbb98e58a91574e76 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 a08e917b30765a13846cbdeab60b83e798c6ae6e..e6d0c3ba1270864c7680d6f745dfd9fdaeb453f3 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -364,6 +364,7 @@ Raúl Cumplido
 Antonio Cuni
 Brian Curtin
 Hakan Celik
+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