mirror of
https://github.com/python/cpython
synced 2024-10-14 10:33:27 +00:00
gh-94943: [Enum] improve repr() when inheriting from a dataclass (GH-99740)
Co-authored-by: C.A.M. Gerlach <CAM.Gerlach@Gerlach.CAM>
This commit is contained in:
parent
5da5aa4c3e
commit
679efbb080
|
@ -459,6 +459,31 @@ sense to allow sharing some common behavior between a group of enumerations.
|
|||
(See `OrderedEnum`_ for an example.)
|
||||
|
||||
|
||||
.. _enum-dataclass-support:
|
||||
|
||||
Dataclass support
|
||||
-----------------
|
||||
|
||||
When inheriting from a :class:`~dataclasses.dataclass`,
|
||||
the :meth:`~Enum.__repr__` omits the inherited class' name. For example::
|
||||
|
||||
>>> @dataclass
|
||||
... class CreatureDataMixin:
|
||||
... size: str
|
||||
... legs: int
|
||||
... tail: bool = field(repr=False, default=True)
|
||||
...
|
||||
>>> class Creature(CreatureDataMixin, Enum):
|
||||
... BEETLE = 'small', 6
|
||||
... DOG = 'medium', 4
|
||||
...
|
||||
>>> Creature.DOG
|
||||
<Creature.DOG: size='medium', legs=4>
|
||||
|
||||
Use the :func:`!dataclass` argument ``repr=False``
|
||||
to use the standard :func:`repr`.
|
||||
|
||||
|
||||
Pickling
|
||||
--------
|
||||
|
||||
|
|
|
@ -389,6 +389,8 @@ Data Types
|
|||
Using :class:`auto` with :class:`Enum` results in integers of increasing value,
|
||||
starting with ``1``.
|
||||
|
||||
.. versionchanged:: 3.12 Added :ref:`enum-dataclass-support`
|
||||
|
||||
|
||||
.. class:: IntEnum
|
||||
|
||||
|
|
18
Lib/enum.py
18
Lib/enum.py
|
@ -955,7 +955,15 @@ def _find_data_repr_(mcls, class_name, bases):
|
|||
return base._value_repr_
|
||||
elif '__repr__' in base.__dict__:
|
||||
# this is our data repr
|
||||
return base.__dict__['__repr__']
|
||||
# double-check if a dataclass with a default __repr__
|
||||
if (
|
||||
'__dataclass_fields__' in base.__dict__
|
||||
and '__dataclass_params__' in base.__dict__
|
||||
and base.__dict__['__dataclass_params__'].repr
|
||||
):
|
||||
return _dataclass_repr
|
||||
else:
|
||||
return base.__dict__['__repr__']
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
|
@ -1551,6 +1559,14 @@ def _power_of_two(value):
|
|||
return False
|
||||
return value == 2 ** _high_bit(value)
|
||||
|
||||
def _dataclass_repr(self):
|
||||
dcf = self.__dataclass_fields__
|
||||
return ', '.join(
|
||||
'%s=%r' % (k, getattr(self, k))
|
||||
for k in dcf.keys()
|
||||
if dcf[k].repr
|
||||
)
|
||||
|
||||
def global_enum_repr(self):
|
||||
"""
|
||||
use module.enum_name instead of class.enum_name
|
||||
|
|
|
@ -2717,17 +2717,67 @@ def upper(self):
|
|||
|
||||
def test_repr_with_dataclass(self):
|
||||
"ensure dataclass-mixin has correct repr()"
|
||||
from dataclasses import dataclass
|
||||
@dataclass
|
||||
#
|
||||
# check overridden dataclass __repr__ is used
|
||||
#
|
||||
from dataclasses import dataclass, field
|
||||
@dataclass(repr=False)
|
||||
class Foo:
|
||||
__qualname__ = 'Foo'
|
||||
a: int
|
||||
def __repr__(self):
|
||||
return 'ha hah!'
|
||||
class Entries(Foo, Enum):
|
||||
ENTRY1 = 1
|
||||
self.assertTrue(isinstance(Entries.ENTRY1, Foo))
|
||||
self.assertTrue(Entries._member_type_ is Foo, Entries._member_type_)
|
||||
self.assertTrue(Entries.ENTRY1.value == Foo(1), Entries.ENTRY1.value)
|
||||
self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: Foo(a=1)>')
|
||||
self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: ha hah!>')
|
||||
#
|
||||
# check auto-generated dataclass __repr__ is not used
|
||||
#
|
||||
@dataclass
|
||||
class CreatureDataMixin:
|
||||
__qualname__ = 'CreatureDataMixin'
|
||||
size: str
|
||||
legs: int
|
||||
tail: bool = field(repr=False, default=True)
|
||||
class Creature(CreatureDataMixin, Enum):
|
||||
__qualname__ = 'Creature'
|
||||
BEETLE = ('small', 6)
|
||||
DOG = ('medium', 4)
|
||||
self.assertEqual(repr(Creature.DOG), "<Creature.DOG: size='medium', legs=4>")
|
||||
#
|
||||
# check inherited repr used
|
||||
#
|
||||
class Huh:
|
||||
def __repr__(self):
|
||||
return 'inherited'
|
||||
@dataclass(repr=False)
|
||||
class CreatureDataMixin(Huh):
|
||||
__qualname__ = 'CreatureDataMixin'
|
||||
size: str
|
||||
legs: int
|
||||
tail: bool = field(repr=False, default=True)
|
||||
class Creature(CreatureDataMixin, Enum):
|
||||
__qualname__ = 'Creature'
|
||||
BEETLE = ('small', 6)
|
||||
DOG = ('medium', 4)
|
||||
self.assertEqual(repr(Creature.DOG), "<Creature.DOG: inherited>")
|
||||
#
|
||||
# check default object.__repr__ used if nothing provided
|
||||
#
|
||||
@dataclass(repr=False)
|
||||
class CreatureDataMixin:
|
||||
__qualname__ = 'CreatureDataMixin'
|
||||
size: str
|
||||
legs: int
|
||||
tail: bool = field(repr=False, default=True)
|
||||
class Creature(CreatureDataMixin, Enum):
|
||||
__qualname__ = 'Creature'
|
||||
BEETLE = ('small', 6)
|
||||
DOG = ('medium', 4)
|
||||
self.assertRegex(repr(Creature.DOG), "<Creature.DOG: .*CreatureDataMixin object at .*>")
|
||||
|
||||
def test_repr_with_init_data_type_mixin(self):
|
||||
# non-data_type is a mixin that doesn't define __new__
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Add :ref:`enum-dataclass-support` to the
|
||||
:class:`~enum.Enum` :meth:`~enum.Enum.__repr__`.
|
||||
When inheriting from a :class:`~dataclasses.dataclass`,
|
||||
only show the field names in the value section of the member :func:`repr`,
|
||||
and not the dataclass' class name.
|
Loading…
Reference in a new issue