[Python-checkins] gh-93464: [Enum] fix auto() failure during multiple assignment (GH-99148)

ethanfurman webhook-mailer at python.org
Sat Nov 5 21:01:20 EDT 2022


https://github.com/python/cpython/commit/8feb7ab77c80968a6de6079299a39b0494b1701b
commit: 8feb7ab77c80968a6de6079299a39b0494b1701b
branch: main
author: Ethan Furman <ethan at stoneleaf.us>
committer: ethanfurman <ethan at stoneleaf.us>
date: 2022-11-05T18:01:08-07:00
summary:

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')`

files:
A Misc/NEWS.d/next/Library/2022-11-05-23-16-15.gh-issue-93464.ucd4vP.rst
M Doc/library/enum.rst
M Lib/enum.py
M Lib/test/test_enum.py

diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
index dcced28b8508..d481f058fbec 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 (``<auto instance>, -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 a66c344dbc3d..c1ccf53dc639 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 f50017d916f5..38b1d7c77146 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 000000000000..6393ec614353
--- /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')``.



More information about the Python-checkins mailing list