]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-93910: [Enum] remove member.member deprecation (GH-103236)
authorEthan Furman <ethan@stoneleaf.us>
Thu, 6 Apr 2023 00:33:52 +0000 (17:33 -0700)
committerGitHub <noreply@github.com>
Thu, 6 Apr 2023 00:33:52 +0000 (17:33 -0700)
i.e. Color.RED.BLUE is now officially supported.

Doc/howto/enum.rst
Lib/enum.py
Lib/test/test_enum.py
Misc/NEWS.d/next/Library/2023-04-04-12-43-38.gh-issue-93910.jurMzv.rst [new file with mode: 0644]

index 38951ed7348f56400af1b5bc6af299f0bf6dcfcd..9390faded2fb8d56ecfaf424b77648d6891854ed 100644 (file)
@@ -988,12 +988,11 @@ but remain normal attributes.
 """"""""""""""""""""
 
 Enum members are instances of their enum class, and are normally accessed as
-``EnumClass.member``.  In Python versions starting with ``3.5`` you could access
-members from other members -- this practice is discouraged, is deprecated
-in ``3.12``, and will be removed in ``3.14``.
+``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.12
 
 
 Creating members that are mixed with other data types
index ec698d5fa3c30053a3581901e15c7692108d0e30..0b0629cf9205068ed815fe1f569e52287300c901 100644 (file)
@@ -201,23 +201,13 @@ class property(DynamicClassAttribute):
                         )
         else:
             if self.fget is None:
-                if self.member is None:   # not sure this can happen, but just in case
+                # 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)
                             )
-                # issue warning deprecating this behavior
-                import warnings
-                warnings.warn(
-                        "`member.member` access (e.g. `Color.RED.BLUE`) is "
-                        "deprecated and will be removed in 3.14.",
-                        DeprecationWarning,
-                        stacklevel=2,
-                        )
-                return self.member
-                # XXX: uncomment in 3.14 and remove warning above
-                # raise AttributeError(
-                #         '%r member has no attribute %r' % (ownerclass, self.name)
-                #         )
             else:
                 return self.fget(instance)
 
@@ -314,22 +304,32 @@ class _proto_member:
                 ):
                 # 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
-        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
-        redirect = property()
-        redirect.member = enum_member
-        redirect.__set_name__(enum_class, member_name)
-        if descriptor:
-            redirect.fget = getattr(descriptor, 'fget', None)
-            redirect.fset = getattr(descriptor, 'fset', None)
-            redirect.fdel = getattr(descriptor, 'fdel', None)
-        setattr(enum_class, member_name, redirect)
+                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)
+            # 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)
         # now add to _member_map_ (even aliases)
         enum_class._member_map_[member_name] = enum_member
         try:
index ee5280601be1840459e4d774c2c86c84ef189258..e4151bf9e6d9b3930bc5d77884fc3e6f78b0f55f 100644 (file)
@@ -2686,28 +2686,15 @@ class TestSpecial(unittest.TestCase):
         self.assertEqual(Private._Private__corporal, 'Radar')
         self.assertEqual(Private._Private__major_, 'Hoolihan')
 
-    @unittest.skipIf(
-            python_version <= (3, 13),
-            'member.member access currently deprecated',
-            )
-    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
-
-    @unittest.skipIf(
-            python_version > (3, 13),
-            'member.member access now raises',
-            )
-    def test_warning_for_member_from_member_access(self):
-        with self.assertWarnsRegex(DeprecationWarning, '`member.member` access .* is deprecated and will be removed in 3.14'):
-            class Di(Enum):
-                YES = 1
-                NO = 0
-            warn = 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 (file)
index 0000000..783aefa
--- /dev/null
@@ -0,0 +1 @@
+Remove deprecation of enum ``memmber.member`` access.