Skip to content

Commit f60b07a

Browse files
authored
bpo-43945: [Enum] reduce scope of new format() behavior (GH-26752)
* [Enum] reduce scope of new format behavior Instead of treating all Enums the same for format(), only user mixed-in enums will be affected. In other words, IntEnum and IntFlag will not be changing the format() behavior, due to the requirement that they be drop-in replacements of existing integer constants. If a user creates their own integer-based enum, then the new behavior will apply: class Grades(int, Enum): A = 5 B = 4 C = 3 D = 2 F = 0 Now: format(Grades.B) -> DeprecationWarning and '4' 3.12: -> no warning, and 'B'
1 parent df1502e commit f60b07a

File tree

5 files changed

+225
-29
lines changed

5 files changed

+225
-29
lines changed

Doc/library/enum.rst

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* :ref:`Advanced Tutorial <enum-advanced-tutorial>`
2323
* :ref:`Enum Cookbook <enum-cookbook>`
2424

25-
----------------
25+
---------------
2626

2727
An enumeration:
2828

@@ -58,6 +58,7 @@ are not normal Python classes. See
5858
:attr:`Color.RED` is ``RED``, the value of :attr:`Color.BLUE` is
5959
``3``, etc.)
6060

61+
---------------
6162

6263
Module Contents
6364
---------------
@@ -73,12 +74,12 @@ Module Contents
7374
:class:`IntEnum`
7475

7576
Base class for creating enumerated constants that are also
76-
subclasses of :class:`int`.
77+
subclasses of :class:`int`. (`Notes`_)
7778

7879
:class:`StrEnum`
7980

8081
Base class for creating enumerated constants that are also
81-
subclasses of :class:`str`.
82+
subclasses of :class:`str`. (`Notes`_)
8283

8384
:class:`Flag`
8485

@@ -89,7 +90,7 @@ Module Contents
8990

9091
Base class for creating enumerated constants that can be combined using
9192
the bitwise operators without losing their :class:`IntFlag` membership.
92-
:class:`IntFlag` members are also subclasses of :class:`int`.
93+
:class:`IntFlag` members are also subclasses of :class:`int`. (`Notes`_)
9394

9495
:class:`EnumCheck`
9596

@@ -132,6 +133,7 @@ Module Contents
132133
.. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto``
133134
.. versionadded:: 3.10 ``StrEnum``, ``EnumCheck``, ``FlagBoundary``
134135

136+
---------------
135137

136138
Data Types
137139
----------
@@ -647,6 +649,7 @@ Data Types
647649

648650
.. versionadded:: 3.10
649651

652+
---------------
650653

651654
Utilites and Decorators
652655
-----------------------
@@ -710,3 +713,25 @@ Utilites and Decorators
710713
on the decorated enumeration.
711714

712715
.. versionadded:: 3.10
716+
717+
---------------
718+
719+
Notes
720+
-----
721+
722+
:class:`IntEnum`, :class:`StrEnum`, and :class:`IntFlag`
723+
724+
These three enum types are designed to be drop-in replacements for existing
725+
integer- and string-based values; as such, they have extra limitations:
726+
727+
- ``format()`` will use the value of the enum member, unless ``__str__``
728+
has been overridden
729+
730+
- ``StrEnum.__str__`` uses the value and not the name of the enum member
731+
732+
If you do not need/want those limitations, you can create your own base
733+
class by mixing in the ``int`` or ``str`` type yourself::
734+
735+
>>> from enum import Enum
736+
>>> class MyIntEnum(int, Enum):
737+
... pass

Lib/asyncio/unix_events.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def add_signal_handler(self, sig, callback, *args):
126126
logger.info('set_wakeup_fd(-1) failed: %s', nexc)
127127

128128
if exc.errno == errno.EINVAL:
129-
raise RuntimeError(f'sig {sig:d} cannot be caught')
129+
raise RuntimeError(f'sig {sig} cannot be caught')
130130
else:
131131
raise
132132

@@ -160,7 +160,7 @@ def remove_signal_handler(self, sig):
160160
signal.signal(sig, handler)
161161
except OSError as exc:
162162
if exc.errno == errno.EINVAL:
163-
raise RuntimeError(f'sig {sig:d} cannot be caught')
163+
raise RuntimeError(f'sig {sig} cannot be caught')
164164
else:
165165
raise
166166

Lib/enum.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -993,9 +993,9 @@ def __format__(self, format_spec):
993993
# mixed-in Enums should use the mixed-in type's __format__, otherwise
994994
# we can get strange results with the Enum name showing up instead of
995995
# the value
996-
996+
#
997997
# pure Enum branch, or branch with __str__ explicitly overridden
998-
str_overridden = type(self).__str__ not in (Enum.__str__, Flag.__str__)
998+
str_overridden = type(self).__str__ not in (Enum.__str__, IntEnum.__str__, Flag.__str__)
999999
if self._member_type_ is object or str_overridden:
10001000
cls = str
10011001
val = str(self)
@@ -1005,7 +1005,7 @@ def __format__(self, format_spec):
10051005
import warnings
10061006
warnings.warn(
10071007
"in 3.12 format() will use the enum member, not the enum member's value;\n"
1008-
"use a format specifier, such as :d for an IntEnum member, to maintain "
1008+
"use a format specifier, such as :d for an integer-based Enum, to maintain "
10091009
"the current display",
10101010
DeprecationWarning,
10111011
stacklevel=2,
@@ -1044,6 +1044,22 @@ class IntEnum(int, Enum):
10441044
Enum where members are also (and must be) ints
10451045
"""
10461046

1047+
def __str__(self):
1048+
return "%s" % (self._name_, )
1049+
1050+
def __format__(self, format_spec):
1051+
"""
1052+
Returns format using actual value unless __str__ has been overridden.
1053+
"""
1054+
str_overridden = type(self).__str__ != IntEnum.__str__
1055+
if str_overridden:
1056+
cls = str
1057+
val = str(self)
1058+
else:
1059+
cls = self._member_type_
1060+
val = self._value_
1061+
return cls.__format__(val, format_spec)
1062+
10471063

10481064
class StrEnum(str, Enum):
10491065
"""
@@ -1072,6 +1088,8 @@ def __new__(cls, *values):
10721088

10731089
__str__ = str.__str__
10741090

1091+
__format__ = str.__format__
1092+
10751093
def _generate_next_value_(name, start, count, last_values):
10761094
"""
10771095
Return the lower-cased version of the member name.
@@ -1300,6 +1318,16 @@ class IntFlag(int, Flag, boundary=EJECT):
13001318
Support for integer-based Flags
13011319
"""
13021320

1321+
def __format__(self, format_spec):
1322+
"""
1323+
Returns format using actual value unless __str__ has been overridden.
1324+
"""
1325+
str_overridden = type(self).__str__ != Flag.__str__
1326+
value = self
1327+
if not str_overridden:
1328+
value = self._value_
1329+
return int.__format__(value, format_spec)
1330+
13031331
def __or__(self, other):
13041332
if isinstance(other, self.__class__):
13051333
other = other._value_

0 commit comments

Comments
 (0)