[Python-checkins] [3.11] gh-93910: [Enum] remove member.member deprecation (GH-103236) (GH-103299)

ethanfurman webhook-mailer at python.org
Thu Apr 6 00:29:22 EDT 2023


https://github.com/python/cpython/commit/58e330ac9cae32f9f44bab24448766085fd29ae6
commit: 58e330ac9cae32f9f44bab24448766085fd29ae6
branch: 3.11
author: Ethan Furman <ethan at stoneleaf.us>
committer: ethanfurman <ethan at stoneleaf.us>
date: 2023-04-05T21:29:14-07:00
summary:

[3.11] gh-93910: [Enum] remove member.member deprecation (GH-103236) (GH-103299)

i.e. Color.RED.BLUE is now officially supported..
(cherry picked from commit 4ec8dd10bd4682793559c4eccbcf6ae00688c4c3)

Co-authored-by: Ethan Furman <ethan at stoneleaf.us>

files:
A Misc/NEWS.d/next/Library/2023-04-04-12-43-38.gh-issue-93910.jurMzv.rst
M Doc/howto/enum.rst
M Lib/enum.py
M Lib/test/test_enum.py

diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst
index f2a10c03fa88..32310692fe56 100644
--- a/Doc/howto/enum.rst
+++ b/Doc/howto/enum.rst
@@ -960,23 +960,11 @@ but remain normal attributes.
 """"""""""""""""""""
 
 Enum members are instances of their enum class, and are normally accessed as
-``EnumClass.member``.  In Python versions ``3.5`` to ``3.10`` you could access
-members from other members -- this practice was discouraged, and in ``3.11``
-:class:`Enum` returns to not allowing it::
-
-    >>> class FieldTypes(Enum):
-    ...     name = 0
-    ...     value = 1
-    ...     size = 2
-    ...
-    >>> FieldTypes.value.size
-    Traceback (most recent call last):
-    ...
-    AttributeError: <enum 'FieldTypes'> member has no attribute 'size'
-
+``EnumClass.member``.  In certain situations, such as writing custom enum
+behavior, being able to access one member directly from another is useful,
+and is supported.
 
 .. versionchanged:: 3.5
-.. versionchanged:: 3.11
 
 
 Creating members that are mixed with other data types
diff --git a/Lib/enum.py b/Lib/enum.py
index 13da287b34aa..84ae339d0d40 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -199,9 +199,13 @@ def __get__(self, instance, ownerclass=None):
                         )
         else:
             if self.fget is None:
-                raise AttributeError(
-                        '%r member has no attribute %r' % (ownerclass, self.name)
-                        )
+                # look for a member by this name.
+                try:
+                    return ownerclass._member_map_[self.name]
+                except KeyError:
+                    raise AttributeError(
+                            '%r has no attribute %r' % (ownerclass, self.name)
+                            ) from None
             else:
                 return self.fget(instance)
 
@@ -298,29 +302,29 @@ def __set_name__(self, enum_class, member_name):
                 ):
                 # no other instances found, record this member in _member_names_
                 enum_class._member_names_.append(member_name)
-        # get redirect in place before adding to _member_map_
-        # but check for other instances in parent classes first
-        need_override = False
-        descriptor = None
+        # if necessary, get redirect in place and then add it to _member_map_
+        found_descriptor = None
         for base in enum_class.__mro__[1:]:
             descriptor = base.__dict__.get(member_name)
             if descriptor is not None:
                 if isinstance(descriptor, (property, DynamicClassAttribute)):
+                    found_descriptor = descriptor
                     break
-                else:
-                    need_override = True
-                    # keep looking for an enum.property
-        if descriptor and not need_override:
-            # previous enum.property found, no further action needed
-            pass
-        elif descriptor and need_override:
+                elif (
+                        hasattr(descriptor, 'fget') and
+                        hasattr(descriptor, 'fset') and
+                        hasattr(descriptor, 'fdel')
+                    ):
+                    found_descriptor = descriptor
+                    continue
+        if found_descriptor:
             redirect = property()
+            redirect.member = enum_member
             redirect.__set_name__(enum_class, member_name)
-            # Previous enum.property found, but some other inherited attribute
-            # is in the way; copy fget, fset, fdel to this one.
-            redirect.fget = descriptor.fget
-            redirect.fset = descriptor.fset
-            redirect.fdel = descriptor.fdel
+            # earlier descriptor found; copy fget, fset, fdel to this one.
+            redirect.fget = found_descriptor.fget
+            redirect.fset = found_descriptor.fset
+            redirect.fdel = found_descriptor.fdel
             setattr(enum_class, member_name, redirect)
         else:
             setattr(enum_class, member_name, enum_member)
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index 09060733bc51..51dd66bea0c4 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -2621,14 +2621,15 @@ class Private(Enum):
         self.assertEqual(Private._Private__corporal, 'Radar')
         self.assertEqual(Private._Private__major_, 'Hoolihan')
 
-    @unittest.skip("Accessing all values retained for performance reasons, see GH-93910")
-    def test_exception_for_member_from_member_access(self):
-        with self.assertRaisesRegex(AttributeError, "<enum .Di.> member has no attribute .NO."):
-            class Di(Enum):
-                YES = 1
-                NO = 0
-            nope = Di.YES.NO
-
+    def test_member_from_member_access(self):
+        class Di(Enum):
+            YES = 1
+            NO = 0
+            name = 3
+        warn = Di.YES.NO
+        self.assertIs(warn, Di.NO)
+        self.assertIs(Di.name, Di['name'])
+        self.assertEqual(Di.name.name, 'name')
 
     def test_dynamic_members_with_static_methods(self):
         #
diff --git a/Misc/NEWS.d/next/Library/2023-04-04-12-43-38.gh-issue-93910.jurMzv.rst b/Misc/NEWS.d/next/Library/2023-04-04-12-43-38.gh-issue-93910.jurMzv.rst
new file mode 100644
index 000000000000..783aefae0770
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-04-04-12-43-38.gh-issue-93910.jurMzv.rst
@@ -0,0 +1 @@
+Remove deprecation of enum ``memmber.member`` access.



More information about the Python-checkins mailing list