Skip to content

Commit

Permalink
gh-117663: [Enum] fix _simple_enum's detection of aliases (GH-117664)
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanfurman committed Apr 9, 2024
1 parent d5f1139 commit e5521bc
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 29 deletions.
72 changes: 44 additions & 28 deletions Lib/enum.py
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
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
@@ -0,0 +1,2 @@
Fix ``_simple_enum`` to detect aliases when multiple arguments are present
but only one is the member value.

0 comments on commit e5521bc

Please sign in to comment.