Skip to content

Commit ab60fdd

Browse files
committed
[3.11] pythongh-103479: [Enum] require __new__ to be considered a data type (pythonGH-103495)
a mixin must either have a __new__ method, or be a dataclass, to be interpreted as a data-type. (cherry picked from commit a6f9594) Co-authored-by: Ethan Furman <[email protected]>
1 parent e643412 commit ab60fdd

File tree

3 files changed

+14
-8
lines changed

3 files changed

+14
-8
lines changed

Doc/howto/enum.rst

+5-3
Original file line numberDiff line numberDiff line change
@@ -837,17 +837,19 @@ 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__`, or a
841+
:class:`~dataclasses.dataclass`
842+
6. %-style formatting: ``%s`` and ``%r`` call the :class:`Enum` class's
841843
:meth:`__str__` and :meth:`__repr__` respectively; other codes (such as
842844
``%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`,
845+
7. :ref:`Formatted string literals <f-strings>`, :meth:`str.format`,
844846
and :func:`format` will use the enum's :meth:`__str__` method.
845847

846848
.. note::
847849

848850
Because :class:`IntEnum`, :class:`IntFlag`, and :class:`StrEnum` are
849851
designed to be drop-in replacements for existing constants, their
850-
:meth:`__str__` method has been reset to their data types
852+
:meth:`__str__` method has been reset to their data types'
851853
:meth:`__str__` method.
852854

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

Lib/enum.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,7 @@ def _find_data_repr_(mcls, class_name, bases):
981981

982982
@classmethod
983983
def _find_data_type_(mcls, class_name, bases):
984+
# a datatype has a __new__ method, or a __dataclass_fields__ attribute
984985
data_types = set()
985986
base_chain = set()
986987
for chain in bases:
@@ -993,7 +994,7 @@ def _find_data_type_(mcls, class_name, bases):
993994
if base._member_type_ is not object:
994995
data_types.add(base._member_type_)
995996
break
996-
elif '__new__' in base.__dict__ or '__init__' in base.__dict__:
997+
elif '__new__' in base.__dict__:
997998
if isinstance(base, EnumType):
998999
continue
9991000
data_types.add(candidate or base)

Lib/test/test_enum.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -2667,13 +2667,14 @@ class Foo:
26672667
a: int
26682668
class Entries(Foo, Enum):
26692669
ENTRY1 = 1
2670+
self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: ha hah!>')
2671+
self.assertTrue(Entries.ENTRY1.value == Foo(1), Entries.ENTRY1.value)
26702672
self.assertTrue(isinstance(Entries.ENTRY1, Foo))
26712673
self.assertTrue(Entries._member_type_ is Foo, Entries._member_type_)
26722674
self.assertTrue(Entries.ENTRY1.value == Foo(1), Entries.ENTRY1.value)
26732675
self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: Foo(a=1)>')
26742676

2675-
def test_repr_with_init_data_type_mixin(self):
2676-
# non-data_type is a mixin that doesn't define __new__
2677+
def test_repr_with_init_mixin(self):
26772678
class Foo:
26782679
def __init__(self, a):
26792680
self.a = a
@@ -2682,9 +2683,9 @@ def __repr__(self):
26822683
class Entries(Foo, Enum):
26832684
ENTRY1 = 1
26842685
#
2685-
self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: Foo(a=1)>')
2686+
self.assertEqual(repr(Entries.ENTRY1), 'Foo(a=1)')
26862687

2687-
def test_repr_and_str_with_non_data_type_mixin(self):
2688+
def test_repr_and_str_with_no_init_mixin(self):
26882689
# non-data_type is a mixin that doesn't define __new__
26892690
class Foo:
26902691
def __repr__(self):
@@ -2790,6 +2791,8 @@ def __new__(cls, c):
27902791

27912792
def test_init_exception(self):
27922793
class Base:
2794+
def __new__(cls, *args):
2795+
return object.__new__(cls)
27932796
def __init__(self, x):
27942797
raise ValueError("I don't like", x)
27952798
with self.assertRaises(TypeError):

0 commit comments

Comments
 (0)