[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