https://github.com/python/cpython/commit/e5521bcca916c63866f0aa1c4dfb3a315d6ada73
commit: e5521bcca916c63866f0aa1c4dfb3a315d6ada73
branch: main
author: Ethan Furman <[email protected]>
committer: ethanfurman <[email protected]>
date: 2024-04-09T11:31:07-07:00
summary:

gh-117663: [Enum] fix _simple_enum's detection of aliases (GH-117664)

files:
A Misc/NEWS.d/next/Library/2024-04-08-19-12-26.gh-issue-117663.CPfc_p.rst
M Lib/enum.py
M Lib/test/test_enum.py

diff --git a/Lib/enum.py b/Lib/enum.py
index 2a135e1b1f1826..98a49eafbb9897 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -1088,8 +1088,6 @@ def _add_member_(cls, name, member):
             setattr(cls, name, member)
         # now add to _member_map_ (even aliases)
         cls._member_map_[name] = member
-        #
-        cls._member_map_[name] = member
 
 EnumMeta = EnumType         # keep EnumMeta name for backwards compatibility
 
@@ -1802,20 +1800,31 @@ def convert_class(cls):
             for name, value in attrs.items():
                 if isinstance(value, auto) and auto.value is _auto_null:
                     value = gnv(name, 1, len(member_names), gnv_last_values)
-                if value in value2member_map or value in unhashable_values:
+                # create basic member (possibly isolate value for alias check)
+                if use_args:
+                    if not isinstance(value, tuple):
+                        value = (value, )
+                    member = new_member(enum_class, *value)
+                    value = value[0]
+                else:
+                    member = new_member(enum_class)
+                if __new__ is None:
+                    member._value_ = value
+                # now check if alias
+                try:
+                    contained = value2member_map.get(member._value_)
+                except TypeError:
+                    contained = None
+                    if member._value_ in unhashable_values:
+                        for m in enum_class:
+                            if m._value_ == member._value_:
+                                contained = m
+                                break
+                if contained is not None:
                     # an alias to an existing member
-                    enum_class(value)._add_alias_(name)
+                    contained._add_alias_(name)
                 else:
-                    # create the member
-                    if use_args:
-                        if not isinstance(value, tuple):
-                            value = (value, )
-                        member = new_member(enum_class, *value)
-                        value = value[0]
-                    else:
-                        member = new_member(enum_class)
-                    if __new__ is None:
-                        member._value_ = value
+                    # finish creating member
                     member._name_ = name
                     member.__objclass__ = enum_class
                     member.__init__(value)
@@ -1847,24 +1856,31 @@ def convert_class(cls):
                     if value.value is _auto_null:
                         value.value = gnv(name, 1, len(member_names), 
gnv_last_values)
                     value = value.value
+                # create basic member (possibly isolate value for alias check)
+                if use_args:
+                    if not isinstance(value, tuple):
+                        value = (value, )
+                    member = new_member(enum_class, *value)
+                    value = value[0]
+                else:
+                    member = new_member(enum_class)
+                if __new__ is None:
+                    member._value_ = value
+                # now check if alias
                 try:
-                    contained = value in value2member_map
+                    contained = value2member_map.get(member._value_)
                 except TypeError:
-                    contained = value in unhashable_values
-                if contained:
+                    contained = None
+                    if member._value_ in unhashable_values:
+                        for m in enum_class:
+                            if m._value_ == member._value_:
+                                contained = m
+                                break
+                if contained is not None:
                     # an alias to an existing member
-                    enum_class(value)._add_alias_(name)
+                    contained._add_alias_(name)
                 else:
-                    # create the member
-                    if use_args:
-                        if not isinstance(value, tuple):
-                            value = (value, )
-                        member = new_member(enum_class, *value)
-                        value = value[0]
-                    else:
-                        member = new_member(enum_class)
-                    if __new__ is None:
-                        member._value_ = value
+                    # finish creating member
                     member._name_ = name
                     member.__objclass__ = enum_class
                     member.__init__(value)
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index 6418d243db65ce..529dfc62eff680 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -5170,7 +5170,57 @@ class Unhashable:
         self.assertIn('python', Unhashable)
         self.assertEqual(Unhashable.name.value, 'python')
         self.assertEqual(Unhashable.name.name, 'name')
-        _test_simple_enum(Unhashable, Unhashable)
+        _test_simple_enum(CheckedUnhashable, Unhashable)
+        ##
+        class CheckedComplexStatus(IntEnum):
+            def __new__(cls, value, phrase, description=''):
+                obj = int.__new__(cls, value)
+                obj._value_ = value
+                obj.phrase = phrase
+                obj.description = description
+                return obj
+            CONTINUE = 100, 'Continue', 'Request received, please continue'
+            PROCESSING = 102, 'Processing'
+            EARLY_HINTS = 103, 'Early Hints'
+            SOME_HINTS = 103, 'Some Early Hints'
+        #
+        @_simple_enum(IntEnum)
+        class ComplexStatus:
+            def __new__(cls, value, phrase, description=''):
+                obj = int.__new__(cls, value)
+                obj._value_ = value
+                obj.phrase = phrase
+                obj.description = description
+                return obj
+            CONTINUE = 100, 'Continue', 'Request received, please continue'
+            PROCESSING = 102, 'Processing'
+            EARLY_HINTS = 103, 'Early Hints'
+            SOME_HINTS = 103, 'Some Early Hints'
+        _test_simple_enum(CheckedComplexStatus, ComplexStatus)
+        #
+        #
+        class CheckedComplexFlag(IntFlag):
+            def __new__(cls, value, label):
+                obj = int.__new__(cls, value)
+                obj._value_ = value
+                obj.label = label
+                return obj
+            SHIRT = 1, 'upper half'
+            VEST = 1, 'outer upper half'
+            PANTS = 2, 'lower half'
+        self.assertIs(CheckedComplexFlag.SHIRT, CheckedComplexFlag.VEST)
+        #
+        @_simple_enum(IntFlag)
+        class ComplexFlag:
+            def __new__(cls, value, label):
+                obj = int.__new__(cls, value)
+                obj._value_ = value
+                obj.label = label
+                return obj
+            SHIRT = 1, 'upper half'
+            VEST = 1, 'uppert half'
+            PANTS = 2, 'lower half'
+        _test_simple_enum(CheckedComplexFlag, ComplexFlag)
 
 
 class MiscTestCase(unittest.TestCase):
diff --git 
a/Misc/NEWS.d/next/Library/2024-04-08-19-12-26.gh-issue-117663.CPfc_p.rst 
b/Misc/NEWS.d/next/Library/2024-04-08-19-12-26.gh-issue-117663.CPfc_p.rst
new file mode 100644
index 00000000000000..2c7a5224b5a6eb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-04-08-19-12-26.gh-issue-117663.CPfc_p.rst
@@ -0,0 +1,2 @@
+Fix ``_simple_enum`` to detect aliases when multiple arguments are present
+but only one is the member value.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to