summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/howto/enum.rst7
-rw-r--r--Lib/enum.py50
-rw-r--r--Lib/test/test_enum.py29
-rw-r--r--Misc/NEWS.d/next/Library/2023-04-04-12-43-38.gh-issue-93910.jurMzv.rst1
4 files changed, 37 insertions, 50 deletions
diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst
index 38951ed..9390fad 100644
--- a/Doc/howto/enum.rst
+++ b/Doc/howto/enum.rst
@@ -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
diff --git a/Lib/enum.py b/Lib/enum.py
index ec698d5..0b0629c 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -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:
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index ee52806..e4151bf 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -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
index 0000000..783aefa
--- /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.