mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	[3.9] bpo-42567: [Enum] call __init_subclass__ after members are added (GH-23714) (GH-23772)
When creating an Enum, `type.__new__` calls `__init_subclass__`, but at that point the members have not been added.
This patch suppresses the initial call, then manually calls the ancestor `__init_subclass__` before returning the new Enum class.
(cherry picked from commit 6bd94de168)
			
			
This commit is contained in:
		
							parent
							
								
									aba12b67c1
								
							
						
					
					
						commit
						9d1fff1fcd
					
				
					 3 changed files with 103 additions and 3 deletions
				
			
		
							
								
								
									
										32
									
								
								Lib/enum.py
									
										
									
									
									
								
							
							
						
						
									
										32
									
								
								Lib/enum.py
									
										
									
									
									
								
							|  | @ -9,6 +9,14 @@ | |||
|         ] | ||||
| 
 | ||||
| 
 | ||||
| class _NoInitSubclass: | ||||
|     """ | ||||
|     temporary base class to suppress calling __init_subclass__ | ||||
|     """ | ||||
|     @classmethod | ||||
|     def __init_subclass__(cls, **kwds): | ||||
|         pass | ||||
| 
 | ||||
| def _is_descriptor(obj): | ||||
|     """ | ||||
|     Returns True if obj is a descriptor, False otherwise. | ||||
|  | @ -176,7 +184,7 @@ def __prepare__(metacls, cls, bases): | |||
|                     ) | ||||
|         return enum_dict | ||||
| 
 | ||||
|     def __new__(metacls, cls, bases, classdict): | ||||
|     def __new__(metacls, cls, bases, classdict, **kwds): | ||||
|         # an Enum class is final once enumeration items have been defined; it | ||||
|         # cannot be mixed with other types (int, float, etc.) if it has an | ||||
|         # inherited __new__ unless a new __new__ is defined (or the resulting | ||||
|  | @ -211,8 +219,22 @@ def __new__(metacls, cls, bases, classdict): | |||
|         if '__doc__' not in classdict: | ||||
|             classdict['__doc__'] = 'An enumeration.' | ||||
| 
 | ||||
|         # postpone calling __init_subclass__ | ||||
|         if '__init_subclass__' in classdict and classdict['__init_subclass__'] is None: | ||||
|             raise TypeError('%s.__init_subclass__ cannot be None') | ||||
|         # remove current __init_subclass__ so previous one can be found with getattr | ||||
|         new_init_subclass = classdict.pop('__init_subclass__', None) | ||||
|         # create our new Enum type | ||||
|         enum_class = super().__new__(metacls, cls, bases, classdict) | ||||
|         if bases: | ||||
|             bases = (_NoInitSubclass, ) + bases | ||||
|             enum_class = type.__new__(metacls, cls, bases, classdict) | ||||
|             enum_class.__bases__ = enum_class.__bases__[1:] #or (object, ) | ||||
|         else: | ||||
|             enum_class = type.__new__(metacls, cls, bases, classdict) | ||||
|         old_init_subclass = getattr(enum_class, '__init_subclass__', None) | ||||
|         # and restore the new one (if there was one) | ||||
|         if new_init_subclass is not None: | ||||
|             enum_class.__init_subclass__ = classmethod(new_init_subclass) | ||||
|         enum_class._member_names_ = []               # names in definition order | ||||
|         enum_class._member_map_ = {}                 # name->value map | ||||
|         enum_class._member_type_ = member_type | ||||
|  | @ -324,6 +346,9 @@ def __new__(metacls, cls, bases, classdict): | |||
|             if _order_ != enum_class._member_names_: | ||||
|                 raise TypeError('member order does not match _order_') | ||||
| 
 | ||||
|         # finally, call parents' __init_subclass__ | ||||
|         if Enum is not None and old_init_subclass is not None: | ||||
|             old_init_subclass(**kwds) | ||||
|         return enum_class | ||||
| 
 | ||||
|     def __bool__(self): | ||||
|  | @ -701,6 +726,9 @@ def _generate_next_value_(name, start, count, last_values): | |||
|         else: | ||||
|             return start | ||||
| 
 | ||||
|     def __init_subclass__(cls, **kwds): | ||||
|         super().__init_subclass__(**kwds) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def _missing_(cls, value): | ||||
|         return None | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Ethan Furman
						Ethan Furman