Skip to content

Commit 3b929a7

Browse files
authored
[3.11] gh-103479: [Enum] require __new__ to be considered a data type (GH-103495) (GH-103514)
a mixin must either have a __new__ method, or be a dataclass, to be interpreted as a data-type; an __init__ method is not enough (restores pre-3.11 behavior for non-dataclasses). (cherry picked from commit a6f9594) Co-authored-by: Ethan Furman <[email protected]>
1 parent 804a973 commit 3b929a7

File tree

3 files changed

+12
-9
lines changed

3 files changed

+12
-9
lines changed

Doc/howto/enum.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -837,17 +837,18 @@ Some rules:
837837
4. When another data type is mixed in, the :attr:`value` attribute is *not the
838838
same* as the enum member itself, although it is equivalent and will compare
839839
equal.
840-
5. %-style formatting: ``%s`` and ``%r`` call the :class:`Enum` class's
840+
5. A ``data type`` is a mixin that defines :meth:`__new__`.
841+
6. %-style formatting: ``%s`` and ``%r`` call the :class:`Enum` class's
841842
:meth:`__str__` and :meth:`__repr__` respectively; other codes (such as
842843
``%i`` or ``%h`` for IntEnum) treat the enum member as its mixed-in type.
843-
6. :ref:`Formatted string literals <f-strings>`, :meth:`str.format`,
844+
7. :ref:`Formatted string literals <f-strings>`, :meth:`str.format`,
844845
and :func:`format` will use the enum's :meth:`__str__` method.
845846

846847
.. note::
847848

848849
Because :class:`IntEnum`, :class:`IntFlag`, and :class:`StrEnum` are
849850
designed to be drop-in replacements for existing constants, their
850-
:meth:`__str__` method has been reset to their data types
851+
:meth:`__str__` method has been reset to their data types'
851852
:meth:`__str__` method.
852853

853854
When to use :meth:`__new__` vs. :meth:`__init__`

Lib/enum.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,7 @@ def _find_data_repr_(mcls, class_name, bases):
974974

975975
@classmethod
976976
def _find_data_type_(mcls, class_name, bases):
977+
# a datatype has a __new__ method
977978
data_types = set()
978979
base_chain = set()
979980
for chain in bases:
@@ -986,7 +987,7 @@ def _find_data_type_(mcls, class_name, bases):
986987
if base._member_type_ is not object:
987988
data_types.add(base._member_type_)
988989
break
989-
elif '__new__' in base.__dict__ or '__init__' in base.__dict__:
990+
elif '__new__' in base.__dict__ or '__dataclass_fields__' in base.__dict__:
990991
if isinstance(base, EnumType):
991992
continue
992993
data_types.add(candidate or base)

Lib/test/test_enum.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2672,19 +2672,18 @@ class Entries(Foo, Enum):
26722672
self.assertTrue(Entries.ENTRY1.value == Foo(1), Entries.ENTRY1.value)
26732673
self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: Foo(a=1)>')
26742674

2675-
def test_repr_with_init_data_type_mixin(self):
2676-
# non-data_type is a mixin that doesn't define __new__
2675+
def test_repr_with_init_mixin(self):
26772676
class Foo:
26782677
def __init__(self, a):
26792678
self.a = a
26802679
def __repr__(self):
2681-
return f'Foo(a={self.a!r})'
2680+
return 'Foo(a=%r)' % self._value_
26822681
class Entries(Foo, Enum):
26832682
ENTRY1 = 1
26842683
#
2685-
self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: Foo(a=1)>')
2684+
self.assertEqual(repr(Entries.ENTRY1), 'Foo(a=1)')
26862685

2687-
def test_repr_and_str_with_non_data_type_mixin(self):
2686+
def test_repr_and_str_with_no_init_mixin(self):
26882687
# non-data_type is a mixin that doesn't define __new__
26892688
class Foo:
26902689
def __repr__(self):
@@ -2796,6 +2795,8 @@ def __new__(cls, c):
27962795

27972796
def test_init_exception(self):
27982797
class Base:
2798+
def __new__(cls, *args):
2799+
return object.__new__(cls)
27992800
def __init__(self, x):
28002801
raise ValueError("I don't like", x)
28012802
with self.assertRaises(TypeError):

0 commit comments

Comments
 (0)