mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
gh-93464: [Enum] fix auto() failure during multiple assignment (GH-99148)
* fix auto() failure during multiple assignment
i.e. `ONE = auto(), 'text'` will now have `ONE' with the value of `(1,
'text')`. Before it would have been `(<an auto instance>, 'text')`
(cherry picked from commit 8feb7ab77c
)
Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
This commit is contained in:
parent
29c3dc050a
commit
be4bab8c2b
4 changed files with 82 additions and 11 deletions
|
@ -249,8 +249,8 @@ Data Types
|
|||
|
||||
Member values can be anything: :class:`int`, :class:`str`, etc.. If
|
||||
the exact value is unimportant you may use :class:`auto` instances and an
|
||||
appropriate value will be chosen for you. Care must be taken if you mix
|
||||
:class:`auto` with other values.
|
||||
appropriate value will be chosen for you. See :class:`auto` for the
|
||||
details.
|
||||
|
||||
.. attribute:: Enum._ignore_
|
||||
|
||||
|
@ -785,7 +785,16 @@ Utilities and Decorators
|
|||
For *Enum* and *IntEnum* that appropriate value will be the last value plus
|
||||
one; for *Flag* and *IntFlag* it will be the first power-of-two greater
|
||||
than the last value; for *StrEnum* it will be the lower-cased version of the
|
||||
member's name.
|
||||
member's name. Care must be taken if mixing *auto()* with manually specified
|
||||
values.
|
||||
|
||||
*auto* instances are only resolved when at the top level of an assignment:
|
||||
|
||||
* ``FIRST = auto()`` will work (auto() is replaced with ``1``);
|
||||
* ``SECOND = auto(), -2`` will work (auto is replaced with ``2``, so ``2, -2`` is
|
||||
used to create the ``SECOND`` enum member;
|
||||
* ``THREE = [auto(), -3]`` will *not* work (``<auto instance>, -3`` is used to
|
||||
create the ``THREE`` enum member)
|
||||
|
||||
``_generate_next_value_`` can be overridden to customize the values used by
|
||||
*auto*.
|
||||
|
|
25
Lib/enum.py
25
Lib/enum.py
|
@ -171,7 +171,8 @@ class auto:
|
|||
"""
|
||||
Instances are replaced with an appropriate value in Enum class suites.
|
||||
"""
|
||||
value = _auto_null
|
||||
def __init__(self, value=_auto_null):
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
||||
return "auto(%r)" % self.value
|
||||
|
@ -421,14 +422,30 @@ class _EnumDict(dict):
|
|||
elif isinstance(value, member):
|
||||
# unwrap value here -- it will become a member
|
||||
value = value.value
|
||||
non_auto_store = True
|
||||
single = False
|
||||
if isinstance(value, auto):
|
||||
if value.value == _auto_null:
|
||||
value.value = self._generate_next_value(
|
||||
single = True
|
||||
value = (value, )
|
||||
if isinstance(value, tuple):
|
||||
auto_valued = []
|
||||
for v in value:
|
||||
if isinstance(v, auto):
|
||||
non_auto_store = False
|
||||
if v.value == _auto_null:
|
||||
v.value = self._generate_next_value(
|
||||
key, 1, len(self._member_names), self._last_values[:],
|
||||
)
|
||||
self._auto_called = True
|
||||
value = value.value
|
||||
v = v.value
|
||||
self._last_values.append(v)
|
||||
auto_valued.append(v)
|
||||
if single:
|
||||
value = auto_valued[0]
|
||||
else:
|
||||
value = tuple(auto_valued)
|
||||
self._member_names[key] = None
|
||||
if non_auto_store:
|
||||
self._last_values.append(value)
|
||||
super().__setitem__(key, value)
|
||||
|
||||
|
|
|
@ -4121,6 +4121,50 @@ class TestInternals(unittest.TestCase):
|
|||
third = auto()
|
||||
self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes))
|
||||
|
||||
def test_multiple_auto_on_line(self):
|
||||
class Huh(Enum):
|
||||
ONE = auto()
|
||||
TWO = auto(), auto()
|
||||
THREE = auto(), auto(), auto()
|
||||
self.assertEqual(Huh.ONE.value, 1)
|
||||
self.assertEqual(Huh.TWO.value, (2, 3))
|
||||
self.assertEqual(Huh.THREE.value, (4, 5, 6))
|
||||
#
|
||||
class Hah(Enum):
|
||||
def __new__(cls, value, abbr=None):
|
||||
member = object.__new__(cls)
|
||||
member._value_ = value
|
||||
member.abbr = abbr or value[:3].lower()
|
||||
return member
|
||||
def _generate_next_value_(name, start, count, last):
|
||||
return name
|
||||
#
|
||||
MONDAY = auto()
|
||||
TUESDAY = auto()
|
||||
WEDNESDAY = auto(), 'WED'
|
||||
THURSDAY = auto(), 'Thu'
|
||||
FRIDAY = auto()
|
||||
self.assertEqual(Hah.MONDAY.value, 'MONDAY')
|
||||
self.assertEqual(Hah.MONDAY.abbr, 'mon')
|
||||
self.assertEqual(Hah.TUESDAY.value, 'TUESDAY')
|
||||
self.assertEqual(Hah.TUESDAY.abbr, 'tue')
|
||||
self.assertEqual(Hah.WEDNESDAY.value, 'WEDNESDAY')
|
||||
self.assertEqual(Hah.WEDNESDAY.abbr, 'WED')
|
||||
self.assertEqual(Hah.THURSDAY.value, 'THURSDAY')
|
||||
self.assertEqual(Hah.THURSDAY.abbr, 'Thu')
|
||||
self.assertEqual(Hah.FRIDAY.value, 'FRIDAY')
|
||||
self.assertEqual(Hah.FRIDAY.abbr, 'fri')
|
||||
#
|
||||
class Huh(Enum):
|
||||
def _generate_next_value_(name, start, count, last):
|
||||
return count+1
|
||||
ONE = auto()
|
||||
TWO = auto(), auto()
|
||||
THREE = auto(), auto(), auto()
|
||||
self.assertEqual(Huh.ONE.value, 1)
|
||||
self.assertEqual(Huh.TWO.value, (2, 2))
|
||||
self.assertEqual(Huh.THREE.value, (3, 3, 3))
|
||||
|
||||
class TestEnumTypeSubclassing(unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
``enum.auto()`` is now correctly activated when combined with other assignment values. E.g. ``ONE = auto(), 'some text'`` will now evaluate as ``(1, 'some text')``.
|
Loading…
Add table
Add a link
Reference in a new issue