diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index 32310692fe56ed..55f0dfb4c48c38 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -837,17 +837,18 @@ Some rules: 4. When another data type is mixed in, the :attr:`value` attribute is *not the same* as the enum member itself, although it is equivalent and will compare equal. -5. %-style formatting: ``%s`` and ``%r`` call the :class:`Enum` class's +5. A ``data type`` is a mixin that defines :meth:`__new__`. +6. %-style formatting: ``%s`` and ``%r`` call the :class:`Enum` class's :meth:`__str__` and :meth:`__repr__` respectively; other codes (such as ``%i`` or ``%h`` for IntEnum) treat the enum member as its mixed-in type. -6. :ref:`Formatted string literals `, :meth:`str.format`, +7. :ref:`Formatted string literals `, :meth:`str.format`, and :func:`format` will use the enum's :meth:`__str__` method. .. note:: Because :class:`IntEnum`, :class:`IntFlag`, and :class:`StrEnum` are designed to be drop-in replacements for existing constants, their - :meth:`__str__` method has been reset to their data types + :meth:`__str__` method has been reset to their data types' :meth:`__str__` method. When to use :meth:`__new__` vs. :meth:`__init__` diff --git a/Lib/enum.py b/Lib/enum.py index a884b50541e521..26e5c50bf8563c 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -974,6 +974,7 @@ def _find_data_repr_(mcls, class_name, bases): @classmethod def _find_data_type_(mcls, class_name, bases): + # a datatype has a __new__ method data_types = set() base_chain = set() for chain in bases: @@ -986,7 +987,7 @@ def _find_data_type_(mcls, class_name, bases): if base._member_type_ is not object: data_types.add(base._member_type_) break - elif '__new__' in base.__dict__ or '__init__' in base.__dict__: + elif '__new__' in base.__dict__ or '__dataclass_fields__' in base.__dict__: if isinstance(base, EnumType): continue data_types.add(candidate or base) diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index d3b4832e340635..188e1a1747565f 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -2672,19 +2672,18 @@ class Entries(Foo, Enum): self.assertTrue(Entries.ENTRY1.value == Foo(1), Entries.ENTRY1.value) self.assertEqual(repr(Entries.ENTRY1), '') - def test_repr_with_init_data_type_mixin(self): - # non-data_type is a mixin that doesn't define __new__ + def test_repr_with_init_mixin(self): class Foo: def __init__(self, a): self.a = a def __repr__(self): - return f'Foo(a={self.a!r})' + return 'Foo(a=%r)' % self._value_ class Entries(Foo, Enum): ENTRY1 = 1 # - self.assertEqual(repr(Entries.ENTRY1), '') + self.assertEqual(repr(Entries.ENTRY1), 'Foo(a=1)') - def test_repr_and_str_with_non_data_type_mixin(self): + def test_repr_and_str_with_no_init_mixin(self): # non-data_type is a mixin that doesn't define __new__ class Foo: def __repr__(self): @@ -2796,6 +2795,8 @@ def __new__(cls, c): def test_init_exception(self): class Base: + def __new__(cls, *args): + return object.__new__(cls) def __init__(self, x): raise ValueError("I don't like", x) with self.assertRaises(TypeError):