]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-41517: do not allow Enums to be extended (GH-22271)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Wed, 16 Sep 2020 14:30:54 +0000 (07:30 -0700)
committerGitHub <noreply@github.com>
Wed, 16 Sep 2020 14:30:54 +0000 (07:30 -0700)
fix bug that let Enums be extended via multiple inheritance
(cherry picked from commit 3064dbf5df1021e85b507366a7ea448c8895efe7)

Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
Lib/enum.py
Lib/test/test_enum.py
Misc/NEWS.d/next/Library/2020-09-15-22-43-30.bpo-41517.sLBH7g.rst [new file with mode: 0644]

index c892d738f8b3851feb4ca226ec035710bebfeb58..4c1acc6e360bd76bd4df7a50e9fa07e32fb79f36 100644 (file)
@@ -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
@@ -142,7 +144,7 @@ class EnumMeta(type):
         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)
 
@@ -401,7 +403,7 @@ class EnumMeta(type):
         """
         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?
@@ -480,7 +482,14 @@ class EnumMeta(type):
         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.
 
@@ -505,7 +514,7 @@ class EnumMeta(type):
                     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:
index 32ff32cf1756766ba39f9c9f4b9e07f32aa50457..11e88dba6b5b3660a0504ada3b190241feb8e594 100644 (file)
@@ -1000,6 +1000,9 @@ class TestEnum(unittest.TestCase):
                 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):
diff --git a/Misc/NEWS.d/next/Library/2020-09-15-22-43-30.bpo-41517.sLBH7g.rst b/Misc/NEWS.d/next/Library/2020-09-15-22-43-30.bpo-41517.sLBH7g.rst
new file mode 100644 (file)
index 0000000..e765471
--- /dev/null
@@ -0,0 +1 @@
+fix bug allowing Enums to be extended via multiple inheritance