mirror of
https://github.com/python/cpython.git
synced 2025-10-02 05:12:23 +00:00
[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 4ec8dd10bd
)
Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
This commit is contained in:
parent
0291397c57
commit
58e330ac9c
4 changed files with 36 additions and 42 deletions
|
@ -960,23 +960,11 @@ but remain normal attributes.
|
||||||
""""""""""""""""""""
|
""""""""""""""""""""
|
||||||
|
|
||||||
Enum members are instances of their enum class, and are normally accessed as
|
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
|
``EnumClass.member``. In certain situations, such as writing custom enum
|
||||||
members from other members -- this practice was discouraged, and in ``3.11``
|
behavior, being able to access one member directly from another is useful,
|
||||||
:class:`Enum` returns to not allowing it::
|
and is supported.
|
||||||
|
|
||||||
>>> 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'
|
|
||||||
|
|
||||||
|
|
||||||
.. versionchanged:: 3.5
|
.. versionchanged:: 3.5
|
||||||
.. versionchanged:: 3.11
|
|
||||||
|
|
||||||
|
|
||||||
Creating members that are mixed with other data types
|
Creating members that are mixed with other data types
|
||||||
|
|
42
Lib/enum.py
42
Lib/enum.py
|
@ -199,9 +199,13 @@ class property(DynamicClassAttribute):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if self.fget is None:
|
if self.fget is None:
|
||||||
raise AttributeError(
|
# look for a member by this name.
|
||||||
'%r member has no attribute %r' % (ownerclass, self.name)
|
try:
|
||||||
)
|
return ownerclass._member_map_[self.name]
|
||||||
|
except KeyError:
|
||||||
|
raise AttributeError(
|
||||||
|
'%r has no attribute %r' % (ownerclass, self.name)
|
||||||
|
) from None
|
||||||
else:
|
else:
|
||||||
return self.fget(instance)
|
return self.fget(instance)
|
||||||
|
|
||||||
|
@ -298,29 +302,29 @@ class _proto_member:
|
||||||
):
|
):
|
||||||
# no other instances found, record this member in _member_names_
|
# no other instances found, record this member in _member_names_
|
||||||
enum_class._member_names_.append(member_name)
|
enum_class._member_names_.append(member_name)
|
||||||
# get redirect in place before adding to _member_map_
|
# if necessary, get redirect in place and then add it to _member_map_
|
||||||
# but check for other instances in parent classes first
|
found_descriptor = None
|
||||||
need_override = False
|
|
||||||
descriptor = None
|
|
||||||
for base in enum_class.__mro__[1:]:
|
for base in enum_class.__mro__[1:]:
|
||||||
descriptor = base.__dict__.get(member_name)
|
descriptor = base.__dict__.get(member_name)
|
||||||
if descriptor is not None:
|
if descriptor is not None:
|
||||||
if isinstance(descriptor, (property, DynamicClassAttribute)):
|
if isinstance(descriptor, (property, DynamicClassAttribute)):
|
||||||
|
found_descriptor = descriptor
|
||||||
break
|
break
|
||||||
else:
|
elif (
|
||||||
need_override = True
|
hasattr(descriptor, 'fget') and
|
||||||
# keep looking for an enum.property
|
hasattr(descriptor, 'fset') and
|
||||||
if descriptor and not need_override:
|
hasattr(descriptor, 'fdel')
|
||||||
# previous enum.property found, no further action needed
|
):
|
||||||
pass
|
found_descriptor = descriptor
|
||||||
elif descriptor and need_override:
|
continue
|
||||||
|
if found_descriptor:
|
||||||
redirect = property()
|
redirect = property()
|
||||||
|
redirect.member = enum_member
|
||||||
redirect.__set_name__(enum_class, member_name)
|
redirect.__set_name__(enum_class, member_name)
|
||||||
# Previous enum.property found, but some other inherited attribute
|
# earlier descriptor found; copy fget, fset, fdel to this one.
|
||||||
# is in the way; copy fget, fset, fdel to this one.
|
redirect.fget = found_descriptor.fget
|
||||||
redirect.fget = descriptor.fget
|
redirect.fset = found_descriptor.fset
|
||||||
redirect.fset = descriptor.fset
|
redirect.fdel = found_descriptor.fdel
|
||||||
redirect.fdel = descriptor.fdel
|
|
||||||
setattr(enum_class, member_name, redirect)
|
setattr(enum_class, member_name, redirect)
|
||||||
else:
|
else:
|
||||||
setattr(enum_class, member_name, enum_member)
|
setattr(enum_class, member_name, enum_member)
|
||||||
|
|
|
@ -2621,14 +2621,15 @@ class TestSpecial(unittest.TestCase):
|
||||||
self.assertEqual(Private._Private__corporal, 'Radar')
|
self.assertEqual(Private._Private__corporal, 'Radar')
|
||||||
self.assertEqual(Private._Private__major_, 'Hoolihan')
|
self.assertEqual(Private._Private__major_, 'Hoolihan')
|
||||||
|
|
||||||
@unittest.skip("Accessing all values retained for performance reasons, see GH-93910")
|
def test_member_from_member_access(self):
|
||||||
def test_exception_for_member_from_member_access(self):
|
class Di(Enum):
|
||||||
with self.assertRaisesRegex(AttributeError, "<enum .Di.> member has no attribute .NO."):
|
YES = 1
|
||||||
class Di(Enum):
|
NO = 0
|
||||||
YES = 1
|
name = 3
|
||||||
NO = 0
|
warn = Di.YES.NO
|
||||||
nope = 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):
|
def test_dynamic_members_with_static_methods(self):
|
||||||
#
|
#
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Remove deprecation of enum ``memmber.member`` access.
|
Loading…
Add table
Add a link
Reference in a new issue