diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index dcced28b8508e8..d481f058fbec3d 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -242,8 +242,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_ @@ -778,7 +778,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 (``, -3`` is used to + create the ``THREE`` enum member) ``_generate_next_value_`` can be overridden to customize the values used by *auto*. diff --git a/Lib/enum.py b/Lib/enum.py index a66c344dbc3db2..c1ccf53dc63942 100644 --- a/Lib/enum.py +++ b/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 @@ -427,15 +428,31 @@ def __setitem__(self, key, value): 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( - key, 1, len(self._member_names), self._last_values[:], - ) - self._auto_called = True - value = value.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 + 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 - self._last_values.append(value) + if non_auto_store: + self._last_values.append(value) super().__setitem__(key, value) def update(self, members, **more_members): diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index f50017d916f5e3..38b1d7c77146e0 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -4116,6 +4116,50 @@ class Dupes(Enum): 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 diff --git a/Misc/NEWS.d/next/Library/2022-11-05-23-16-15.gh-issue-93464.ucd4vP.rst b/Misc/NEWS.d/next/Library/2022-11-05-23-16-15.gh-issue-93464.ucd4vP.rst new file mode 100644 index 00000000000000..6393ec61435365 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-11-05-23-16-15.gh-issue-93464.ucd4vP.rst @@ -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')``.