Skip to content

Commit e5521bc

Browse files
authored
gh-117663: [Enum] fix _simple_enum's detection of aliases (GH-117664)
1 parent d5f1139 commit e5521bc

File tree

3 files changed

+97
-29
lines changed

3 files changed

+97
-29
lines changed

Lib/enum.py

+44-28
Original file line numberDiff line numberDiff line change
@@ -1088,8 +1088,6 @@ def _add_member_(cls, name, member):
10881088
setattr(cls, name, member)
10891089
# now add to _member_map_ (even aliases)
10901090
cls._member_map_[name] = member
1091-
#
1092-
cls._member_map_[name] = member
10931091

10941092
EnumMeta = EnumType # keep EnumMeta name for backwards compatibility
10951093

@@ -1802,20 +1800,31 @@ def convert_class(cls):
18021800
for name, value in attrs.items():
18031801
if isinstance(value, auto) and auto.value is _auto_null:
18041802
value = gnv(name, 1, len(member_names), gnv_last_values)
1805-
if value in value2member_map or value in unhashable_values:
1803+
# create basic member (possibly isolate value for alias check)
1804+
if use_args:
1805+
if not isinstance(value, tuple):
1806+
value = (value, )
1807+
member = new_member(enum_class, *value)
1808+
value = value[0]
1809+
else:
1810+
member = new_member(enum_class)
1811+
if __new__ is None:
1812+
member._value_ = value
1813+
# now check if alias
1814+
try:
1815+
contained = value2member_map.get(member._value_)
1816+
except TypeError:
1817+
contained = None
1818+
if member._value_ in unhashable_values:
1819+
for m in enum_class:
1820+
if m._value_ == member._value_:
1821+
contained = m
1822+
break
1823+
if contained is not None:
18061824
# an alias to an existing member
1807-
enum_class(value)._add_alias_(name)
1825+
contained._add_alias_(name)
18081826
else:
1809-
# create the member
1810-
if use_args:
1811-
if not isinstance(value, tuple):
1812-
value = (value, )
1813-
member = new_member(enum_class, *value)
1814-
value = value[0]
1815-
else:
1816-
member = new_member(enum_class)
1817-
if __new__ is None:
1818-
member._value_ = value
1827+
# finish creating member
18191828
member._name_ = name
18201829
member.__objclass__ = enum_class
18211830
member.__init__(value)
@@ -1847,24 +1856,31 @@ def convert_class(cls):
18471856
if value.value is _auto_null:
18481857
value.value = gnv(name, 1, len(member_names), gnv_last_values)
18491858
value = value.value
1859+
# create basic member (possibly isolate value for alias check)
1860+
if use_args:
1861+
if not isinstance(value, tuple):
1862+
value = (value, )
1863+
member = new_member(enum_class, *value)
1864+
value = value[0]
1865+
else:
1866+
member = new_member(enum_class)
1867+
if __new__ is None:
1868+
member._value_ = value
1869+
# now check if alias
18501870
try:
1851-
contained = value in value2member_map
1871+
contained = value2member_map.get(member._value_)
18521872
except TypeError:
1853-
contained = value in unhashable_values
1854-
if contained:
1873+
contained = None
1874+
if member._value_ in unhashable_values:
1875+
for m in enum_class:
1876+
if m._value_ == member._value_:
1877+
contained = m
1878+
break
1879+
if contained is not None:
18551880
# an alias to an existing member
1856-
enum_class(value)._add_alias_(name)
1881+
contained._add_alias_(name)
18571882
else:
1858-
# create the member
1859-
if use_args:
1860-
if not isinstance(value, tuple):
1861-
value = (value, )
1862-
member = new_member(enum_class, *value)
1863-
value = value[0]
1864-
else:
1865-
member = new_member(enum_class)
1866-
if __new__ is None:
1867-
member._value_ = value
1883+
# finish creating member
18681884
member._name_ = name
18691885
member.__objclass__ = enum_class
18701886
member.__init__(value)

Lib/test/test_enum.py

+51-1
Original file line numberDiff line numberDiff line change
@@ -5170,7 +5170,57 @@ class Unhashable:
51705170
self.assertIn('python', Unhashable)
51715171
self.assertEqual(Unhashable.name.value, 'python')
51725172
self.assertEqual(Unhashable.name.name, 'name')
5173-
_test_simple_enum(Unhashable, Unhashable)
5173+
_test_simple_enum(CheckedUnhashable, Unhashable)
5174+
##
5175+
class CheckedComplexStatus(IntEnum):
5176+
def __new__(cls, value, phrase, description=''):
5177+
obj = int.__new__(cls, value)
5178+
obj._value_ = value
5179+
obj.phrase = phrase
5180+
obj.description = description
5181+
return obj
5182+
CONTINUE = 100, 'Continue', 'Request received, please continue'
5183+
PROCESSING = 102, 'Processing'
5184+
EARLY_HINTS = 103, 'Early Hints'
5185+
SOME_HINTS = 103, 'Some Early Hints'
5186+
#
5187+
@_simple_enum(IntEnum)
5188+
class ComplexStatus:
5189+
def __new__(cls, value, phrase, description=''):
5190+
obj = int.__new__(cls, value)
5191+
obj._value_ = value
5192+
obj.phrase = phrase
5193+
obj.description = description
5194+
return obj
5195+
CONTINUE = 100, 'Continue', 'Request received, please continue'
5196+
PROCESSING = 102, 'Processing'
5197+
EARLY_HINTS = 103, 'Early Hints'
5198+
SOME_HINTS = 103, 'Some Early Hints'
5199+
_test_simple_enum(CheckedComplexStatus, ComplexStatus)
5200+
#
5201+
#
5202+
class CheckedComplexFlag(IntFlag):
5203+
def __new__(cls, value, label):
5204+
obj = int.__new__(cls, value)
5205+
obj._value_ = value
5206+
obj.label = label
5207+
return obj
5208+
SHIRT = 1, 'upper half'
5209+
VEST = 1, 'outer upper half'
5210+
PANTS = 2, 'lower half'
5211+
self.assertIs(CheckedComplexFlag.SHIRT, CheckedComplexFlag.VEST)
5212+
#
5213+
@_simple_enum(IntFlag)
5214+
class ComplexFlag:
5215+
def __new__(cls, value, label):
5216+
obj = int.__new__(cls, value)
5217+
obj._value_ = value
5218+
obj.label = label
5219+
return obj
5220+
SHIRT = 1, 'upper half'
5221+
VEST = 1, 'uppert half'
5222+
PANTS = 2, 'lower half'
5223+
_test_simple_enum(CheckedComplexFlag, ComplexFlag)
51745224

51755225

51765226
class MiscTestCase(unittest.TestCase):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix ``_simple_enum`` to detect aliases when multiple arguments are present
2+
but only one is the member value.

0 commit comments

Comments
 (0)