From d986d1657e1e7b50807d0633cb31d96a2d866d42 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Tue, 22 Sep 2020 13:00:07 -0700 Subject: [PATCH] bpo-41816: `StrEnum.__str__` is `str.__str__` (GH-22362) use `str.__str__` for `StrEnum` so that `str(StrEnum.member)` is the same as directly accessing the string value of the `StrEnum` member --- Doc/library/enum.rst | 15 +++++++++++++++ Lib/enum.py | 2 ++ Lib/test/test_enum.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 3e9b1f9db35..118002bef19 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -622,6 +622,11 @@ an incorrect member:: Before :class:`StrEnum`, ``Directions.NORTH`` would have been the :class:`tuple` ``('north',)``. +.. note:: + + Unlike other Enum's, ``str(StrEnum.member)`` will return the value of the + member instead of the usual ``"EnumClass.member"``. + .. versionadded:: 3.10 @@ -1243,3 +1248,13 @@ all named flags and all named combinations of flags that are in the value:: >>> Color(7) # not named combination +``StrEnum`` and :meth:`str.__str__` +""""""""""""""""""""""""""""""""""" + +An important difference between :class:`StrEnum` and other Enums is the +:meth:`__str__` method; because :class:`StrEnum` members are strings, some +parts of Python will read the string data directly, while others will call +:meth:`str()`. To make those two operations have the same result, +:meth:`StrEnum.__str__` will be the same as :meth:`str.__str__` so that +``str(StrEnum.member) == StrEnum.member`` is true. + diff --git a/Lib/enum.py b/Lib/enum.py index 589b17fd697..40ff25b9cda 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -718,6 +718,8 @@ class StrEnum(str, Enum): member._value_ = value return member + __str__ = str.__str__ + def _reduce_ex_by_name(self, proto): return self.name diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 8e84d929429..3431040f98a 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -2036,6 +2036,37 @@ class TestEnum(unittest.TestCase): two = '2' three = b'3', 'ascii' four = b'4', 'latin1', 'strict' + self.assertEqual(GoodStrEnum.one, '1') + self.assertEqual(str(GoodStrEnum.one), '1') + self.assertEqual(GoodStrEnum.one, str(GoodStrEnum.one)) + self.assertEqual(GoodStrEnum.one, '{}'.format(GoodStrEnum.one)) + # + class DumbMixin: + def __str__(self): + return "don't do this" + class DumbStrEnum(DumbMixin, StrEnum): + five = '5' + six = '6' + seven = '7' + self.assertEqual(DumbStrEnum.seven, '7') + self.assertEqual(str(DumbStrEnum.seven), "don't do this") + # + class EnumMixin(Enum): + def hello(self): + print('hello from %s' % (self, )) + class HelloEnum(EnumMixin, StrEnum): + eight = '8' + self.assertEqual(HelloEnum.eight, '8') + self.assertEqual(HelloEnum.eight, str(HelloEnum.eight)) + # + class GoodbyeMixin: + def goodbye(self): + print('%s wishes you a fond farewell') + class GoodbyeEnum(GoodbyeMixin, EnumMixin, StrEnum): + nine = '9' + self.assertEqual(GoodbyeEnum.nine, '9') + self.assertEqual(GoodbyeEnum.nine, str(GoodbyeEnum.nine)) + # with self.assertRaisesRegex(TypeError, '1 is not a string'): class FirstFailedStrEnum(StrEnum): one = 1