Mailing List Archive

gh-117663: [Enum] fix _simple_enum's detection of aliases (GH-117664)
https://github.com/python/cpython/commit/e5521bcca916c63866f0aa1c4dfb3a315d6ada73
commit: e5521bcca916c63866f0aa1c4dfb3a315d6ada73
branch: main
author: Ethan Furman <ethan@stoneleaf.us>
committer: ethanfurman <ethan@stoneleaf.us>
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 -- python-checkins@python.org
To unsubscribe send an email to python-checkins-leave@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: list-python-checkins@lists.gossamer-threads.com