8000 gh-117663: [Enum] fix _simple_enum's detection of aliases by ethanfurman · Pull Request #117664 · python/cpython · GitHub
[go: up one dir, main page]

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 1 commit
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 = None
contained = value2member_map.get(member._value_)
except TypeError:
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):
8000 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 = None
contained = value2member_map.get(member._value_)
except TypeError:
contained = value in unhashable_values
if contained:
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
1 change: 0 additions & 1 deletion Lib/http/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ class HTTPStatus:
def __new__(cls, value, phrase, description=''):
obj = int.__new__(cls, value)
obj._value_ = value

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whitespace change can probably be omitted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, but the blank line irritates me. :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm nitpicking, but that means you're making a change to HTTPStatus while this PR is not about HTTPStatus but about Enums. It's a whitespace change, but still.. I've made it now part of my PR https://github.com/python/cpython/pull/117611/files which already changes the file Lib/http/__init__.py -- so you can drop it from this PR if you want.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. Change removed.

obj.phrase = phrase
obj.description = description
return obj
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'
67E6 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.
0