Skip to content

Commit

Permalink
[3.11] pythongh-93910: [Enum] remove member.member deprecation (pyth…
Browse files Browse the repository at this point in the history
…onGH-103236)

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

                    Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
  • Loading branch information
ethanfurman committed Apr 6, 2023
1 parent 851e744 commit e573165
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 42 deletions.
18 changes: 3 additions & 15 deletions Doc/howto/enum.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 23 additions & 19 deletions Lib/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
else:
return self.fget(instance)

Expand Down Expand Up @@ -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)
Expand Down
17 changes: 9 additions & 8 deletions Lib/test/test_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
#
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove deprecation of enum ``memmber.member`` access.

0 comments on commit e573165

Please sign in to comment.