Skip to content

[3.8] bpo-41517: do not allow Enums to be extended (GH-22271) #22279

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 1 commit into from
Sep 16, 2020
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
19 changes: 14 additions & 5 deletions Lib/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,12 @@ class EnumMeta(type):
"""Metaclass for Enum"""
@classmethod
def __prepare__(metacls, cls, bases):
# check that previous enum members do not exist
metacls._check_for_existing_members(cls, bases)
# create the namespace dict
enum_dict = _EnumDict()
# inherit previous flags and _generate_next_value_ function
member_type, first_enum = metacls._get_mixins_(bases)
member_type, first_enum = metacls._get_mixins_(cls, bases)
if first_enum is not None:
enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None)
return enum_dict
Expand All @@ -142,7 +144,7 @@ def __new__(metacls, cls, bases, classdict):
ignore = classdict['_ignore_']
for key in ignore:
classdict.pop(key, None)
member_type, first_enum = metacls._get_mixins_(bases)
member_type, first_enum = metacls._get_mixins_(cls, bases)
__new__, save_new, use_args = metacls._find_new_(classdict, member_type,
first_enum)

Expand Down Expand Up @@ -401,7 +403,7 @@ def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, s
"""
metacls = cls.__class__
bases = (cls, ) if type is None else (type, cls)
_, first_enum = cls._get_mixins_(bases)
_, first_enum = cls._get_mixins_(cls, bases)
classdict = metacls.__prepare__(class_name, bases)

# special processing needed for names?
Expand Down Expand Up @@ -480,7 +482,14 @@ def _convert(cls, *args, **kwargs):
return cls._convert_(*args, **kwargs)

@staticmethod
def _get_mixins_(bases):
def _check_for_existing_members(class_name, bases):
for chain in bases:
for base in chain.__mro__:
if issubclass(base, Enum) and base._member_names_:
raise TypeError("%s: cannot extend enumeration %r" % (class_name, base.__name__))

@staticmethod
def _get_mixins_(class_name, bases):
"""Returns the type for creating enum members, and the first inherited
enum class.

Expand All @@ -505,7 +514,7 @@ def _find_data_type(bases):
elif not issubclass(base, Enum):
candidate = base
if len(data_types) > 1:
raise TypeError('too many data types: %r' % data_types)
raise TypeError('%r: too many data types: %r' % (class_name, data_types))
elif data_types:
return data_types[0]
else:
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,9 @@ class MoreColor(Color):
cyan = 4
magenta = 5
yellow = 6
with self.assertRaisesRegex(TypeError, "EvenMoreColor: cannot extend enumeration 'Color'"):
class EvenMoreColor(Color, IntEnum):
chartruese = 7

def test_exclude_methods(self):
class whatever(Enum):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fix bug allowing Enums to be extended via multiple inheritance