mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-41517: do not allow Enums to be extended (GH-22271)
fix bug that let Enums be extended via multiple inheritance
(cherry picked from commit 3064dbf5df)
Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
			
			
This commit is contained in:
		
							parent
							
								
									6a39888c2c
								
							
						
					
					
						commit
						48f99250ff
					
				
					 3 changed files with 18 additions and 5 deletions
				
			
		
							
								
								
									
										19
									
								
								Lib/enum.py
									
										
									
									
									
								
							
							
						
						
									
										19
									
								
								Lib/enum.py
									
										
									
									
									
								
							|  | @ -123,10 +123,12 @@ class EnumMeta(type): | ||||||
|     """Metaclass for Enum""" |     """Metaclass for Enum""" | ||||||
|     @classmethod |     @classmethod | ||||||
|     def __prepare__(metacls, cls, bases): |     def __prepare__(metacls, cls, bases): | ||||||
|  |         # check that previous enum members do not exist | ||||||
|  |         metacls._check_for_existing_members(cls, bases) | ||||||
|         # create the namespace dict |         # create the namespace dict | ||||||
|         enum_dict = _EnumDict() |         enum_dict = _EnumDict() | ||||||
|         # inherit previous flags and _generate_next_value_ function |         # 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: |         if first_enum is not None: | ||||||
|             enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None) |             enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None) | ||||||
|         return enum_dict |         return enum_dict | ||||||
|  | @ -142,7 +144,7 @@ def __new__(metacls, cls, bases, classdict): | ||||||
|         ignore = classdict['_ignore_'] |         ignore = classdict['_ignore_'] | ||||||
|         for key in ignore: |         for key in ignore: | ||||||
|             classdict.pop(key, None) |             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, |         __new__, save_new, use_args = metacls._find_new_(classdict, member_type, | ||||||
|                                                         first_enum) |                                                         first_enum) | ||||||
| 
 | 
 | ||||||
|  | @ -401,7 +403,7 @@ def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, s | ||||||
|         """ |         """ | ||||||
|         metacls = cls.__class__ |         metacls = cls.__class__ | ||||||
|         bases = (cls, ) if type is None else (type, cls) |         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) |         classdict = metacls.__prepare__(class_name, bases) | ||||||
| 
 | 
 | ||||||
|         # special processing needed for names? |         # special processing needed for names? | ||||||
|  | @ -474,7 +476,14 @@ def _convert_(cls, name, module, filter, source=None): | ||||||
|         return cls |         return cls | ||||||
| 
 | 
 | ||||||
|     @staticmethod |     @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 |         """Returns the type for creating enum members, and the first inherited | ||||||
|         enum class. |         enum class. | ||||||
| 
 | 
 | ||||||
|  | @ -499,7 +508,7 @@ def _find_data_type(bases): | ||||||
|                     elif not issubclass(base, Enum): |                     elif not issubclass(base, Enum): | ||||||
|                         candidate = base |                         candidate = base | ||||||
|             if len(data_types) > 1: |             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: |             elif data_types: | ||||||
|                 return data_types[0] |                 return data_types[0] | ||||||
|             else: |             else: | ||||||
|  |  | ||||||
|  | @ -1001,6 +1001,9 @@ class MoreColor(Color): | ||||||
|                 cyan = 4 |                 cyan = 4 | ||||||
|                 magenta = 5 |                 magenta = 5 | ||||||
|                 yellow = 6 |                 yellow = 6 | ||||||
|  |         with self.assertRaisesRegex(TypeError, "EvenMoreColor: cannot extend enumeration 'Color'"): | ||||||
|  |             class EvenMoreColor(Color, IntEnum): | ||||||
|  |                 chartruese = 7 | ||||||
| 
 | 
 | ||||||
|     def test_exclude_methods(self): |     def test_exclude_methods(self): | ||||||
|         class whatever(Enum): |         class whatever(Enum): | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | fix bug allowing Enums to be extended via multiple inheritance | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Miss Islington (bot)
						Miss Islington (bot)