Skip to content

Commit be4bab8

Browse files
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 8feb7ab) Co-authored-by: Ethan Furman <[email protected]>
1 parent 29c3dc0 commit be4bab8

File tree

4 files changed

+82
-11
lines changed

4 files changed

+82
-11
lines changed

Doc/library/enum.rst

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,8 @@ Data Types
249249

250250
Member values can be anything: :class:`int`, :class:`str`, etc.. If
251251
the exact value is unimportant you may use :class:`auto` instances and an
252-
appropriate value will be chosen for you. Care must be taken if you mix
253-
:class:`auto` with other values.
252+
appropriate value will be chosen for you. See :class:`auto` for the
253+
details.
254254

255255
.. attribute:: Enum._ignore_
256256

@@ -785,7 +785,16 @@ Utilities and Decorators
785785
For *Enum* and *IntEnum* that appropriate value will be the last value plus
786786
one; for *Flag* and *IntFlag* it will be the first power-of-two greater
787787
than the last value; for *StrEnum* it will be the lower-cased version of the
788-
member's name.
788+
member's name. Care must be taken if mixing *auto()* with manually specified
789+
values.
790+
791+
*auto* instances are only resolved when at the top level of an assignment:
792+
793+
* ``FIRST = auto()`` will work (auto() is replaced with ``1``);
794+
* ``SECOND = auto(), -2`` will work (auto is replaced with ``2``, so ``2, -2`` is
795+
used to create the ``SECOND`` enum member;
796+
* ``THREE = [auto(), -3]`` will *not* work (``<auto instance>, -3`` is used to
797+
create the ``THREE`` enum member)
789798

790799
``_generate_next_value_`` can be overridden to customize the values used by
791800
*auto*.

Lib/enum.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ class auto:
171171
"""
172172
Instances are replaced with an appropriate value in Enum class suites.
173173
"""
174-
value = _auto_null
174+
def __init__(self, value=_auto_null):
175+
self.value = value
175176

176177
def __repr__(self):
177178
return "auto(%r)" % self.value
@@ -421,15 +422,31 @@ def __setitem__(self, key, value):
421422
elif isinstance(value, member):
422423
# unwrap value here -- it will become a member
423424
value = value.value
425+
non_auto_store = True
426+
single = False
424427
if isinstance(value, auto):
425-
if value.value == _auto_null:
426-
value.value = self._generate_next_value(
427-
key, 1, len(self._member_names), self._last_values[:],
428-
)
429-
self._auto_called = True
430-
value = value.value
428+
single = True
429+
value = (value, )
430+
if isinstance(value, tuple):
431+
auto_valued = []
432+
for v in value:
433+
if isinstance(v, auto):
434+
non_auto_store = False
435+
if v.value == _auto_null:
436+
v.value = self._generate_next_value(
437+
key, 1, len(self._member_names), self._last_values[:],
438+
)
439+
self._auto_called = True
440+
v = v.value
441+
self._last_values.append(v)
442+
auto_valued.append(v)
443+
if single:
444+
value = auto_valued[0]
445+
else:
446+
value = tuple(auto_valued)
431447
self._member_names[key] = None
432-
self._last_values.append(value)
448+
if non_auto_store:
449+
self._last_values.append(value)
433450
super().__setitem__(key, value)
434451

435452
def update(self, members, **more_members):

Lib/test/test_enum.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4121,6 +4121,50 @@ class Dupes(Enum):
41214121
third = auto()
41224122
self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes))
41234123

4124+
def test_multiple_auto_on_line(self):
4125+
class Huh(Enum):
4126+
ONE = auto()
4127+
TWO = auto(), auto()
4128+
THREE = auto(), auto(), auto()
4129+
self.assertEqual(Huh.ONE.value, 1)
4130+
self.assertEqual(Huh.TWO.value, (2, 3))
4131+
self.assertEqual(Huh.THREE.value, (4, 5, 6))
4132+
#
4133+
class Hah(Enum):
4134+
def __new__(cls, value, abbr=None):
4135+
member = object.__new__(cls)
4136+
member._value_ = value
4137+
member.abbr = abbr or value[:3].lower()
4138+
return member
4139+
def _generate_next_value_(name, start, count, last):
4140+
return name
4141+
#
4142+
MONDAY = auto()
4143+
TUESDAY = auto()
4144+
WEDNESDAY = auto(), 'WED'
4145+
THURSDAY = auto(), 'Thu'
4146+
FRIDAY = auto()
4147+
self.assertEqual(Hah.MONDAY.value, 'MONDAY')
4148+
self.assertEqual(Hah.MONDAY.abbr, 'mon')
4149+
self.assertEqual(Hah.TUESDAY.value, 'TUESDAY')
4150+
self.assertEqual(Hah.TUESDAY.abbr, 'tue')
4151+
self.assertEqual(Hah.WEDNESDAY.value, 'WEDNESDAY')
4152+
self.assertEqual(Hah.WEDNESDAY.abbr, 'WED')
4153+
self.assertEqual(Hah.THURSDAY.value, 'THURSDAY')
4154+
self.assertEqual(Hah.THURSDAY.abbr, 'Thu')
4155+
self.assertEqual(Hah.FRIDAY.value, 'FRIDAY')
4156+
self.assertEqual(Hah.FRIDAY.abbr, 'fri')
4157+
#
4158+
class Huh(Enum):
4159+
def _generate_next_value_(name, start, count, last):
4160+
return count+1
4161+
ONE = auto()
4162+
TWO = auto(), auto()
4163+
THREE = auto(), auto(), auto()
4164+
self.assertEqual(Huh.ONE.value, 1)
4165+
self.assertEqual(Huh.TWO.value, (2, 2))
4166+
self.assertEqual(Huh.THREE.value, (3, 3, 3))
4167+
41244168
class TestEnumTypeSubclassing(unittest.TestCase):
41254169
pass
41264170

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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')``.

0 commit comments

Comments
 (0)