Skip to content

gh-117663: [Enum] fix _simple_enum's detection of aliases #117664

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 44 additions & 28 deletions Lib/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
52 changes: 51 additions & 1 deletion Lib/test/test_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix ``_simple_enum`` to detect aliases when multiple arguments are present
but only one is the member value.
Loading