mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	gh-112328: Make EnumDict usable on its own and document it (GH-123669)
Co-authored-by: Rafi <rafi.promit@gmail.com> Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) <wk.cvs.github@sydorenko.org.ua> Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
This commit is contained in:
		
							parent
							
								
									3879ca0100
								
							
						
					
					
						commit
						2a66dd33df
					
				
					 5 changed files with 60 additions and 16 deletions
				
			
		|  | @ -110,6 +110,10 @@ Module Contents | |||
|       ``KEEP`` which allows for more fine-grained control over how invalid values | ||||
|       are dealt with in an enumeration. | ||||
| 
 | ||||
|    :class:`EnumDict` | ||||
| 
 | ||||
|       A subclass of :class:`dict` for use when subclassing :class:`EnumType`. | ||||
| 
 | ||||
|    :class:`auto` | ||||
| 
 | ||||
|       Instances are replaced with an appropriate value for Enum members. | ||||
|  | @ -149,14 +153,10 @@ Module Contents | |||
| 
 | ||||
|       Return a list of all power-of-two integers contained in a flag. | ||||
| 
 | ||||
|    :class:`EnumDict` | ||||
| 
 | ||||
|       A subclass of :class:`dict` for use when subclassing :class:`EnumType`. | ||||
| 
 | ||||
| 
 | ||||
| .. versionadded:: 3.6  ``Flag``, ``IntFlag``, ``auto`` | ||||
| .. versionadded:: 3.11  ``StrEnum``, ``EnumCheck``, ``ReprEnum``, ``FlagBoundary``, ``property``, ``member``, ``nonmember``, ``global_enum``, ``show_flag_values`` | ||||
| .. versionadded:: 3.14  ``EnumDict`` | ||||
| .. versionadded:: 3.13  ``EnumDict`` | ||||
| 
 | ||||
| --------------- | ||||
| 
 | ||||
|  | @ -830,13 +830,23 @@ Data Types | |||
| 
 | ||||
| .. class:: EnumDict | ||||
| 
 | ||||
|    *EnumDict* is a subclass of :class:`dict` for use when subclassing :class:`EnumType`. | ||||
|    *EnumDict* is a subclass of :class:`dict` that is used as the namespace | ||||
|    for defining enum classes (see :ref:`prepare`). | ||||
|    It is exposed to allow subclasses of :class:`EnumType` with advanced | ||||
|    behavior like having multiple values per member. | ||||
|    It should be called with the name of the enum class being created, otherwise | ||||
|    private names and internal classes will not be handled correctly. | ||||
| 
 | ||||
|    Note that only the :class:`~collections.abc.MutableMapping` interface | ||||
|    (:meth:`~object.__setitem__` and :meth:`~dict.update`) is overridden. | ||||
|    It may be possible to bypass the checks using other :class:`!dict` | ||||
|    operations like :meth:`|= <object.__ior__>`. | ||||
| 
 | ||||
|    .. attribute:: EnumDict.member_names | ||||
| 
 | ||||
|       Return list of member names. | ||||
|       A list of member names. | ||||
| 
 | ||||
|    .. versionadded:: 3.14 | ||||
|    .. versionadded:: 3.13 | ||||
| 
 | ||||
| --------------- | ||||
| 
 | ||||
|  |  | |||
|  | @ -879,11 +879,13 @@ email | |||
|   (Contributed by Thomas Dwyer and Victor Stinner for :gh:`102988` to improve | ||||
|   the :cve:`2023-27043` fix.) | ||||
| 
 | ||||
| 
 | ||||
| enum | ||||
| ---- | ||||
| 
 | ||||
| * :class:`~enum.EnumDict` has been made public in :mod:`enum` to better support | ||||
|   subclassing :class:`~enum.EnumType`. | ||||
| * :class:`~enum.EnumDict` has been made public to better support subclassing | ||||
|   :class:`~enum.EnumType`. | ||||
| 
 | ||||
| 
 | ||||
| fractions | ||||
| --------- | ||||
|  |  | |||
							
								
								
									
										10
									
								
								Lib/enum.py
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								Lib/enum.py
									
										
									
									
									
								
							|  | @ -342,12 +342,13 @@ class EnumDict(dict): | |||
|     EnumType will use the names found in self._member_names as the | ||||
|     enumeration member names. | ||||
|     """ | ||||
|     def __init__(self): | ||||
|     def __init__(self, cls_name=None): | ||||
|         super().__init__() | ||||
|         self._member_names = {} # use a dict -- faster look-up than a list, and keeps insertion order since 3.7 | ||||
|         self._last_values = [] | ||||
|         self._ignore = [] | ||||
|         self._auto_called = False | ||||
|         self._cls_name = cls_name | ||||
| 
 | ||||
|     def __setitem__(self, key, value): | ||||
|         """ | ||||
|  | @ -358,7 +359,7 @@ def __setitem__(self, key, value): | |||
| 
 | ||||
|         Single underscore (sunder) names are reserved. | ||||
|         """ | ||||
|         if _is_private(self._cls_name, key): | ||||
|         if self._cls_name is not None and _is_private(self._cls_name, key): | ||||
|             # do nothing, name will be a normal attribute | ||||
|             pass | ||||
|         elif _is_sunder(key): | ||||
|  | @ -406,7 +407,7 @@ def __setitem__(self, key, value): | |||
|             value = value.value | ||||
|         elif _is_descriptor(value): | ||||
|             pass | ||||
|         elif _is_internal_class(self._cls_name, value): | ||||
|         elif self._cls_name is not None and _is_internal_class(self._cls_name, value): | ||||
|             # do nothing, name will be a normal attribute | ||||
|             pass | ||||
|         else: | ||||
|  | @ -478,8 +479,7 @@ def __prepare__(metacls, cls, bases, **kwds): | |||
|         # check that previous enum members do not exist | ||||
|         metacls._check_for_existing_members_(cls, bases) | ||||
|         # create the namespace dict | ||||
|         enum_dict = EnumDict() | ||||
|         enum_dict._cls_name = cls | ||||
|         enum_dict = EnumDict(cls) | ||||
|         # inherit previous flags and _generate_next_value_ function | ||||
|         member_type, first_enum = metacls._get_mixins_(cls, bases) | ||||
|         if first_enum is not None: | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
| from enum import Enum, EnumMeta, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto | ||||
| from enum import STRICT, CONFORM, EJECT, KEEP, _simple_enum, _test_simple_enum | ||||
| from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS, ReprEnum | ||||
| from enum import member, nonmember, _iter_bits_lsb | ||||
| from enum import member, nonmember, _iter_bits_lsb, EnumDict | ||||
| from io import StringIO | ||||
| from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL | ||||
| from test import support | ||||
|  | @ -5440,6 +5440,37 @@ def test_convert_repr_and_str(self): | |||
|         self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5') | ||||
| 
 | ||||
| 
 | ||||
| class TestEnumDict(unittest.TestCase): | ||||
|     def test_enum_dict_in_metaclass(self): | ||||
|         """Test that EnumDict is usable as a class namespace""" | ||||
|         class Meta(type): | ||||
|             @classmethod | ||||
|             def __prepare__(metacls, cls, bases, **kwds): | ||||
|                 return EnumDict(cls) | ||||
| 
 | ||||
|         class MyClass(metaclass=Meta): | ||||
|             a = 1 | ||||
| 
 | ||||
|             with self.assertRaises(TypeError): | ||||
|                 a = 2  # duplicate | ||||
| 
 | ||||
|             with self.assertRaises(ValueError): | ||||
|                 _a_sunder_ = 3 | ||||
| 
 | ||||
|     def test_enum_dict_standalone(self): | ||||
|         """Test that EnumDict is usable on its own""" | ||||
|         enumdict = EnumDict() | ||||
|         enumdict['a'] = 1 | ||||
| 
 | ||||
|         with self.assertRaises(TypeError): | ||||
|             enumdict['a'] = 'other value' | ||||
| 
 | ||||
|         # Only MutableMapping interface is overridden for now. | ||||
|         # If this stops passing, update the documentation. | ||||
|         enumdict |= {'a': 'other value'} | ||||
|         self.assertEqual(enumdict['a'], 'other value') | ||||
| 
 | ||||
| 
 | ||||
| # helpers | ||||
| 
 | ||||
| def enum_dir(cls): | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| :class:`enum.EnumDict` can now be used without resorting to private API. | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Petr Viktorin
						Petr Viktorin