Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
issue deprecation warning for member.member access
  • Loading branch information
ethanfurman committed Jul 21, 2022
commit a81c0e54fdcc876cec9a73eb2fbc5b39e3a7c489
46 changes: 31 additions & 15 deletions Lib/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,23 @@ def __get__(self, instance, ownerclass=None):
)
else:
if self.fget is None:
raise AttributeError(
'%r member has no attribute %r' % (ownerclass, self.name)
if self.member is None: # not sure this can happen, but just in case
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)

Expand Down Expand Up @@ -301,23 +315,19 @@ def __set_name__(self, enum_class, member_name):
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
for base in enum_class.__mro__[1:]:
descriptor = base.__dict__.get(member_name)
if descriptor is not None:
if isinstance(descriptor, (property, DynamicClassAttribute)):
break
else:
need_override = True
# keep looking for an enum.property
redirect = property()
redirect.member = enum_member
redirect.__set_name__(enum_class, member_name)
if descriptor:
redirect.fget = descriptor.fget
redirect.fset = descriptor.fset
redirect.fdel = descriptor.fdel
redirect.fget = getattr(descriptor, 'fget', None)
redirect.fset = getattr(descriptor, 'fset', None)
redirect.fdel = getattr(descriptor, 'fdel', None)
setattr(enum_class, member_name, redirect)
# now add to _member_map_ (even aliases)
enum_class._member_map_[member_name] = enum_member
Expand Down Expand Up @@ -1180,10 +1190,10 @@ def __reduce_ex__(self, proto):
# enum.property is used to provide access to the `name` and
# `value` attributes of enum members while keeping some measure of
# protection from modification, while still allowing for an enumeration
# to have members named `name` and `value`. This works because enumeration
# members are not set directly on the enum class; they are kept in a
# separate structure, _member_map_, which is where enum.property looks for
# them
# to have members named `name` and `value`. This works because each
# instance of enum.property saves its companion member, which it returns
# on class lookup; on instance lookup it either executes a provided function
# or raises an AttributeError.

@property
def name(self):
Expand Down Expand Up @@ -1657,10 +1667,12 @@ def convert_class(cls):
value = gnv(name, 1, len(member_names), gnv_last_values)
if value in value2member_map:
# an alias to an existing member
member = value2member_map[value]
redirect = property()
redirect.member = member
redirect.__set_name__(enum_class, name)
setattr(enum_class, name, redirect)
member_map[name] = value2member_map[value]
member_map[name] = member
else:
# create the member
if use_args:
Expand All @@ -1676,6 +1688,7 @@ def convert_class(cls):
member.__objclass__ = enum_class
member.__init__(value)
redirect = property()
redirect.member = member
redirect.__set_name__(enum_class, name)
setattr(enum_class, name, redirect)
member_map[name] = member
Expand Down Expand Up @@ -1703,10 +1716,12 @@ def convert_class(cls):
value = value.value
if value in value2member_map:
# an alias to an existing member
member = value2member_map[value]
redirect = property()
redirect.member = member
redirect.__set_name__(enum_class, name)
setattr(enum_class, name, redirect)
member_map[name] = value2member_map[value]
member_map[name] = member
else:
# create the member
if use_args:
Expand All @@ -1723,6 +1738,7 @@ def convert_class(cls):
member.__init__(value)
member._sort_order_ = len(member_names)
redirect = property()
redirect.member = member
redirect.__set_name__(enum_class, name)
setattr(enum_class, name, redirect)
member_map[name] = member
Expand Down
16 changes: 15 additions & 1 deletion Lib/test/test_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -2646,14 +2646,28 @@ 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")
@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
self.assertIs(warn, Di.NO)

def test_dynamic_members_with_static_methods(self):
#
Expand Down