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 |       ``KEEP`` which allows for more fine-grained control over how invalid values | ||||||
|       are dealt with in an enumeration. |       are dealt with in an enumeration. | ||||||
| 
 | 
 | ||||||
|  |    :class:`EnumDict` | ||||||
|  | 
 | ||||||
|  |       A subclass of :class:`dict` for use when subclassing :class:`EnumType`. | ||||||
|  | 
 | ||||||
|    :class:`auto` |    :class:`auto` | ||||||
| 
 | 
 | ||||||
|       Instances are replaced with an appropriate value for Enum members. |       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. |       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.6  ``Flag``, ``IntFlag``, ``auto`` | ||||||
| .. versionadded:: 3.11  ``StrEnum``, ``EnumCheck``, ``ReprEnum``, ``FlagBoundary``, ``property``, ``member``, ``nonmember``, ``global_enum``, ``show_flag_values`` | .. 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 | .. 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 |    .. 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 |   (Contributed by Thomas Dwyer and Victor Stinner for :gh:`102988` to improve | ||||||
|   the :cve:`2023-27043` fix.) |   the :cve:`2023-27043` fix.) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| enum | enum | ||||||
| ---- | ---- | ||||||
| 
 | 
 | ||||||
| * :class:`~enum.EnumDict` has been made public in :mod:`enum` to better support | * :class:`~enum.EnumDict` has been made public to better support subclassing | ||||||
|   subclassing :class:`~enum.EnumType`. |   :class:`~enum.EnumType`. | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| fractions | 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 |     EnumType will use the names found in self._member_names as the | ||||||
|     enumeration member names. |     enumeration member names. | ||||||
|     """ |     """ | ||||||
|     def __init__(self): |     def __init__(self, cls_name=None): | ||||||
|         super().__init__() |         super().__init__() | ||||||
|         self._member_names = {} # use a dict -- faster look-up than a list, and keeps insertion order since 3.7 |         self._member_names = {} # use a dict -- faster look-up than a list, and keeps insertion order since 3.7 | ||||||
|         self._last_values = [] |         self._last_values = [] | ||||||
|         self._ignore = [] |         self._ignore = [] | ||||||
|         self._auto_called = False |         self._auto_called = False | ||||||
|  |         self._cls_name = cls_name | ||||||
| 
 | 
 | ||||||
|     def __setitem__(self, key, value): |     def __setitem__(self, key, value): | ||||||
|         """ |         """ | ||||||
|  | @ -358,7 +359,7 @@ def __setitem__(self, key, value): | ||||||
| 
 | 
 | ||||||
|         Single underscore (sunder) names are reserved. |         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 |             # do nothing, name will be a normal attribute | ||||||
|             pass |             pass | ||||||
|         elif _is_sunder(key): |         elif _is_sunder(key): | ||||||
|  | @ -406,7 +407,7 @@ def __setitem__(self, key, value): | ||||||
|             value = value.value |             value = value.value | ||||||
|         elif _is_descriptor(value): |         elif _is_descriptor(value): | ||||||
|             pass |             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 |             # do nothing, name will be a normal attribute | ||||||
|             pass |             pass | ||||||
|         else: |         else: | ||||||
|  | @ -478,8 +479,7 @@ def __prepare__(metacls, cls, bases, **kwds): | ||||||
|         # check that previous enum members do not exist |         # check that previous enum members do not exist | ||||||
|         metacls._check_for_existing_members_(cls, bases) |         metacls._check_for_existing_members_(cls, bases) | ||||||
|         # create the namespace dict |         # create the namespace dict | ||||||
|         enum_dict = EnumDict() |         enum_dict = EnumDict(cls) | ||||||
|         enum_dict._cls_name = cls |  | ||||||
|         # inherit previous flags and _generate_next_value_ function |         # inherit previous flags and _generate_next_value_ function | ||||||
|         member_type, first_enum = metacls._get_mixins_(cls, bases) |         member_type, first_enum = metacls._get_mixins_(cls, bases) | ||||||
|         if first_enum is not None: |         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 Enum, EnumMeta, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto | ||||||
| from enum import STRICT, CONFORM, EJECT, KEEP, _simple_enum, _test_simple_enum | from enum import STRICT, CONFORM, EJECT, KEEP, _simple_enum, _test_simple_enum | ||||||
| from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS, ReprEnum | 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 io import StringIO | ||||||
| from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL | from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL | ||||||
| from test import support | 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') |         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 | # helpers | ||||||
| 
 | 
 | ||||||
| def enum_dir(cls): | 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