From d00adac946aa136019f7c7ee68c0a230c4179111 Mon Sep 17 00:00:00 2001 From: Rafi Date: Sun, 14 Jul 2024 10:17:19 +0200 Subject: [PATCH 01/10] Document EnumDict in docs and release notes --- Doc/library/enum.rst | 20 +++++++++++++++++++- Doc/whatsnew/3.13.rst | 5 +++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 8b3f397ea862f4..8192d270208381 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -64,6 +64,10 @@ are not normal Python classes. See Module Contents --------------- + :class:`EnumDict` + + An enum class :class:`dict` that tracks order and enforces unique member names. + :class:`EnumType` The ``type`` for Enum and its subclasses. @@ -159,7 +163,21 @@ Data Types ---------- -.. class:: EnumType +.. class:: EnumDict + + *EnumDict* is used by *EnumType* to keep track of the enum member orders and prevent reusing the member names. + + .. attribute:: EnumDict.member_names + + Return list of member names. + + .. method:: EnumDict.__setitem__(self, key, value) + + Set any item as an enum member that is not dundered and not a descriptor. + + .. method:: EnumDict.update(self, members, **more_members) + + Update the dictionary from the given iterable or dictionary members and more_members. *EnumType* is the :term:`metaclass` for *enum* enumerations. It is possible to subclass *EnumType* -- see :ref:`Subclassing EnumType ` diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 4a9a0b77d068b3..97e64abc9c8beb 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -742,6 +742,11 @@ 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`. + fractions --------- From 3bff0b954adddedc766cf8309d06b62e7d9e139f Mon Sep 17 00:00:00 2001 From: Rafi Date: Sun, 14 Jul 2024 10:26:43 +0200 Subject: [PATCH 02/10] Restore deleted EnumType --- Doc/library/enum.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 8192d270208381..d6f14745bb5fb2 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -183,6 +183,8 @@ Data Types to subclass *EnumType* -- see :ref:`Subclassing EnumType ` for details. +.. class:: EnumType + ``EnumType`` is responsible for setting the correct :meth:`!__repr__`, :meth:`!__str__`, :meth:`!__format__`, and :meth:`!__reduce__` methods on the final *enum*, as well as creating the enum members, properly handling From 11e2fd194b6c74f0c0b2f7c7d1babb52a488a33a Mon Sep 17 00:00:00 2001 From: Md Rokibul Islam Date: Tue, 16 Jul 2024 23:37:19 +0200 Subject: [PATCH 03/10] Update Doc/library/enum.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) --- Doc/library/enum.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index d6f14745bb5fb2..2d1d8c0cf1f680 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -179,12 +179,12 @@ Data Types Update the dictionary from the given iterable or dictionary members and more_members. +.. class:: EnumType + *EnumType* is the :term:`metaclass` for *enum* enumerations. It is possible to subclass *EnumType* -- see :ref:`Subclassing EnumType ` for details. -.. class:: EnumType - ``EnumType`` is responsible for setting the correct :meth:`!__repr__`, :meth:`!__str__`, :meth:`!__format__`, and :meth:`!__reduce__` methods on the final *enum*, as well as creating the enum members, properly handling From e8c368ef9bf2ae1ac2eda0a62650ef28d00fd50c Mon Sep 17 00:00:00 2001 From: Rafi Date: Sun, 28 Jul 2024 22:08:54 +0200 Subject: [PATCH 04/10] Update description --- Doc/library/enum.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 2d1d8c0cf1f680..426d0b86ff32bd 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -66,7 +66,7 @@ Module Contents :class:`EnumDict` - An enum class :class:`dict` that tracks order and enforces unique member names. + A subclass of :class:`dict` that tracks order and enforces unique member names. :class:`EnumType` @@ -165,7 +165,7 @@ Data Types .. class:: EnumDict - *EnumDict* is used by *EnumType* to keep track of the enum member orders and prevent reusing the member names. + *EnumDict* is a subclass of :class:`dict` that keeps track of the order of enum members and prevents reusing member names. Use *EnumDict* when member names must be unique and their order needs to be preserved. .. attribute:: EnumDict.member_names From 05ea9bd80d2b00008ea8e9565ce2063b58951883 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 4 Sep 2024 13:26:40 +0200 Subject: [PATCH 05/10] Put EnumDict docs on the buttom to de-emphasize the class --- Doc/library/enum.rst | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 426d0b86ff32bd..02afa6bbeeb105 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -64,10 +64,6 @@ are not normal Python classes. See Module Contents --------------- - :class:`EnumDict` - - A subclass of :class:`dict` that tracks order and enforces unique member names. - :class:`EnumType` The ``type`` for Enum and its subclasses. @@ -114,6 +110,11 @@ 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` that tracks order and enforces unique + member names. + :class:`auto` Instances are replaced with an appropriate value for Enum members. @@ -163,22 +164,6 @@ Data Types ---------- -.. class:: EnumDict - - *EnumDict* is a subclass of :class:`dict` that keeps track of the order of enum members and prevents reusing member names. Use *EnumDict* when member names must be unique and their order needs to be preserved. - - .. attribute:: EnumDict.member_names - - Return list of member names. - - .. method:: EnumDict.__setitem__(self, key, value) - - Set any item as an enum member that is not dundered and not a descriptor. - - .. method:: EnumDict.update(self, members, **more_members) - - Update the dictionary from the given iterable or dictionary members and more_members. - .. class:: EnumType *EnumType* is the :term:`metaclass` for *enum* enumerations. It is possible @@ -841,6 +826,23 @@ Data Types .. versionadded:: 3.11 +.. class:: EnumDict + + *EnumDict* is a subclass of :class:`dict` that keeps track of the order of enum members and prevents reusing member names. Use *EnumDict* when member names must be unique and their order needs to be preserved. + + .. attribute:: EnumDict.member_names + + Return list of member names. + + .. method:: EnumDict.__setitem__(self, key, value) + + Set any item as an enum member that is not dundered and not a descriptor. + + .. method:: EnumDict.update(self, members, **more_members) + + Update the dictionary from the given iterable or dictionary members and more_members. + + --------------- Supported ``__dunder__`` names From 3e88296783966f5fc40ee584a6713665cc2a2462 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 4 Sep 2024 13:27:13 +0200 Subject: [PATCH 06/10] Add a versionadded entry, and properly indent a previous one --- Doc/library/enum.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 02afa6bbeeb105..ff95a48423d029 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -824,7 +824,7 @@ Data Types >>> KeepFlag(2**2 + 2**4) -.. versionadded:: 3.11 + .. versionadded:: 3.11 .. class:: EnumDict @@ -842,6 +842,7 @@ Data Types Update the dictionary from the given iterable or dictionary members and more_members. + .. versionadded:: 3.13 --------------- From 162ebf01865307b27d9fd2f20eb79eb24d5e47bb Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 4 Sep 2024 14:11:56 +0200 Subject: [PATCH 07/10] Reword the documentation --- Doc/library/enum.rst | 22 ++++++++++--------- Doc/whatsnew/3.13.rst | 4 +++- ...-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst | 2 ++ 3 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index ff95a48423d029..4d07837bf3574b 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -828,19 +828,21 @@ Data Types .. class:: EnumDict - *EnumDict* is a subclass of :class:`dict` that keeps track of the order of enum members and prevents reusing member names. Use *EnumDict* when member names must be unique and their order needs to be preserved. + *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 prevents reusing member names, with special behavior for names that + start with an underscore. + + 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:`|= `. .. attribute:: EnumDict.member_names - Return list of member names. - - .. method:: EnumDict.__setitem__(self, key, value) - - Set any item as an enum member that is not dundered and not a descriptor. - - .. method:: EnumDict.update(self, members, **more_members) - - Update the dictionary from the given iterable or dictionary members and more_members. + A list of member names. .. versionadded:: 3.13 diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 97e64abc9c8beb..197fac9c897190 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -745,7 +745,9 @@ email enum ---- -* :class:`~enum.EnumDict` has been made public in :mod:`enum`. +* :class:`enum.EnumDict` has been made public, in order to allow subclasses + of :class:`~enum.EnumType` with advanced behavior like having multiple values + per member. fractions --------- diff --git a/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst b/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst new file mode 100644 index 00000000000000..5a6ccfe85e3a3c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst @@ -0,0 +1,2 @@ +:class:`enum.EnumDict` can now be used on its own, without resorting to +private API. From 5d9c81b34ad2415611bbb0221b00c4a096bbc93c Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 4 Sep 2024 14:12:28 +0200 Subject: [PATCH 08/10] Allow usage of EnumDict without setting a private attr. Add tests. --- Lib/enum.py | 5 +++-- Lib/test/test_enum.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/Lib/enum.py b/Lib/enum.py index c36fc75a24a239..5576c46840050c 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -346,6 +346,7 @@ def __init__(self): self._last_values = [] self._ignore = [] self._auto_called = False + self._cls_name = None def __setitem__(self, key, value): """ @@ -356,7 +357,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): @@ -404,7 +405,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: diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 99fd16ba361e6f..2f3711390f7dcc 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -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 @@ -5414,6 +5414,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() + + 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 starts passing, update the documentation. + enumdict |= {'a': 'other value'} + self.assertEqual(enumdict['a'], 'other value') + + # helpers def enum_dir(cls): From 694d8f73997c469a5c32453b8dd73df7e87c7f4b Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Thu, 19 Dec 2024 16:23:47 -0800 Subject: [PATCH 09/10] updates --- Doc/library/enum.rst | 9 ++------- Doc/whatsnew/3.13.rst | 10 ++-------- Lib/enum.py | 11 +++++------ Lib/test/test_enum.py | 6 +++--- .../2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst | 3 +-- 5 files changed, 13 insertions(+), 26 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index c78be60277b214..1cc9b507201262 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -112,8 +112,7 @@ Module Contents :class:`EnumDict` - A subclass of :class:`dict` that tracks order and enforces unique - member names. + A subclass of :class:`dict` for use when subclassing :class:`EnumType`. :class:`auto` @@ -154,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`` --------------- diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 7935f25504f38a..c8e0f94f4246fb 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -879,18 +879,12 @@ 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`. enum ---- -* :class:`enum.EnumDict` has been made public, in order to allow subclasses - of :class:`~enum.EnumType` with advanced behavior like having multiple values - per member. +* :class:`~enum.EnumDict` has been made public to better support subclassing + :class:`~enum.EnumType`. fractions diff --git a/Lib/enum.py b/Lib/enum.py index 17d83c732441d1..f6ce29533e07b3 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -342,13 +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): 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 = None + self._cls_name = cls_name def __setitem__(self, key, value): """ @@ -359,7 +359,7 @@ def __setitem__(self, key, value): Single underscore (sunder) names are reserved. """ - if self._cls_name is not None and _is_private(self._cls_name, key): + if _is_private(self._cls_name, key): # do nothing, name will be a normal attribute pass elif _is_sunder(key): @@ -407,7 +407,7 @@ def __setitem__(self, key, value): value = value.value elif _is_descriptor(value): pass - elif self._cls_name is not None and _is_internal_class(self._cls_name, value): + elif _is_internal_class(self._cls_name, value): # do nothing, name will be a normal attribute pass else: @@ -479,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: diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 22e397f333bee6..fa12b8513b3250 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -5446,7 +5446,7 @@ def test_enum_dict_in_metaclass(self): class Meta(type): @classmethod def __prepare__(metacls, cls, bases, **kwds): - return EnumDict() + return EnumDict(cls) class MyClass(metaclass=Meta): a = 1 @@ -5459,14 +5459,14 @@ class MyClass(metaclass=Meta): def test_enum_dict_standalone(self): """Test that EnumDict is usable on its own""" - enumdict = EnumDict() + enumdict = EnumDict('test') enumdict['a'] = 1 with self.assertRaises(TypeError): enumdict['a'] = 'other value' # Only MutableMapping interface is overridden for now. - # If this starts passing, update the documentation. + # If this stops passing, update the documentation. enumdict |= {'a': 'other value'} self.assertEqual(enumdict['a'], 'other value') diff --git a/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst b/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst index 5a6ccfe85e3a3c..96da94a9f211af 100644 --- a/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst +++ b/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst @@ -1,2 +1 @@ -:class:`enum.EnumDict` can now be used on its own, without resorting to -private API. +:class:`enum.EnumDict` can now be used without resorting to private API. From 9d6908c157c6fa25e5b93a960e14f13f3570f5fd Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Thu, 19 Dec 2024 16:46:57 -0800 Subject: [PATCH 10/10] more fixes --- Doc/library/enum.rst | 4 ++-- Lib/enum.py | 6 +++--- Lib/test/test_enum.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 1cc9b507201262..8ca949368db4ff 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -834,8 +834,8 @@ Data Types 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 prevents reusing member names, with special behavior for names that - start with an underscore. + 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. diff --git a/Lib/enum.py b/Lib/enum.py index f6ce29533e07b3..04443471b40bff 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -342,7 +342,7 @@ class EnumDict(dict): EnumType will use the names found in self._member_names as the enumeration member names. """ - def __init__(self, cls_name): + 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 = [] @@ -359,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): @@ -407,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: diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index fa12b8513b3250..8884295b1ab89c 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -5459,7 +5459,7 @@ class MyClass(metaclass=Meta): def test_enum_dict_standalone(self): """Test that EnumDict is usable on its own""" - enumdict = EnumDict('test') + enumdict = EnumDict() enumdict['a'] = 1 with self.assertRaises(TypeError): pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy