mirror of https://github.com/python/cpython.git
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
|
@ -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…
Reference in New Issue