mirror of
https://github.com/python/cpython.git
synced 2025-10-06 15:11:58 +00:00
gh-78157: [Enum] nested classes will not be members in 3.13 (GH-92366)
- add member() and nonmember() functions - add deprecation warning for internal classes in enums not becoming members in 3.13 Co-authored-by: edwardcwang
This commit is contained in:
parent
fa4f0a134e
commit
93364f9716
5 changed files with 219 additions and 4 deletions
57
Lib/enum.py
57
Lib/enum.py
|
@ -8,7 +8,7 @@ from functools import reduce
|
|||
__all__ = [
|
||||
'EnumType', 'EnumMeta',
|
||||
'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag', 'ReprEnum',
|
||||
'auto', 'unique', 'property', 'verify',
|
||||
'auto', 'unique', 'property', 'verify', 'member', 'nonmember',
|
||||
'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
|
||||
'global_flag_repr', 'global_enum_repr', 'global_str', 'global_enum',
|
||||
'EnumCheck', 'CONTINUOUS', 'NAMED_FLAGS', 'UNIQUE',
|
||||
|
@ -20,6 +20,20 @@ __all__ = [
|
|||
# This is also why there are checks in EnumType like `if Enum is not None`
|
||||
Enum = Flag = EJECT = _stdlib_enums = ReprEnum = None
|
||||
|
||||
class nonmember(object):
|
||||
"""
|
||||
Protects item from becaming an Enum member during class creation.
|
||||
"""
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
class member(object):
|
||||
"""
|
||||
Forces item to became an Enum member during class creation.
|
||||
"""
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def _is_descriptor(obj):
|
||||
"""
|
||||
Returns True if obj is a descriptor, False otherwise.
|
||||
|
@ -52,6 +66,15 @@ def _is_sunder(name):
|
|||
name[-2:-1] != '_'
|
||||
)
|
||||
|
||||
def _is_internal_class(cls_name, obj):
|
||||
# do not use `re` as `re` imports `enum`
|
||||
if not isinstance(obj, type):
|
||||
return False
|
||||
qualname = getattr(obj, '__qualname__', '')
|
||||
s_pattern = cls_name + '.' + getattr(obj, '__name__', '')
|
||||
e_pattern = '.' + s_pattern
|
||||
return qualname == s_pattern or qualname.endswith(e_pattern)
|
||||
|
||||
def _is_private(cls_name, name):
|
||||
# do not use `re` as `re` imports `enum`
|
||||
pattern = '_%s__' % (cls_name, )
|
||||
|
@ -139,14 +162,20 @@ def _dedent(text):
|
|||
lines[j] = l[i:]
|
||||
return '\n'.join(lines)
|
||||
|
||||
class _auto_null:
|
||||
def __repr__(self):
|
||||
return '_auto_null'
|
||||
_auto_null = _auto_null()
|
||||
|
||||
_auto_null = object()
|
||||
class auto:
|
||||
"""
|
||||
Instances are replaced with an appropriate value in Enum class suites.
|
||||
"""
|
||||
value = _auto_null
|
||||
|
||||
def __repr__(self):
|
||||
return "auto(%r)" % self.value
|
||||
|
||||
class property(DynamicClassAttribute):
|
||||
"""
|
||||
This is a descriptor, used to define attributes that act differently
|
||||
|
@ -325,8 +354,16 @@ class _EnumDict(dict):
|
|||
|
||||
Single underscore (sunder) names are reserved.
|
||||
"""
|
||||
if _is_internal_class(self._cls_name, value):
|
||||
import warnings
|
||||
warnings.warn(
|
||||
"In 3.13 classes created inside an enum will not become a member. "
|
||||
"Use the `member` decorator to keep the current behavior.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
if _is_private(self._cls_name, key):
|
||||
# do nothing, name will be a normal attribute
|
||||
# also do nothing, name will be a normal attribute
|
||||
pass
|
||||
elif _is_sunder(key):
|
||||
if key not in (
|
||||
|
@ -364,10 +401,22 @@ class _EnumDict(dict):
|
|||
raise TypeError('%r already defined as %r' % (key, self[key]))
|
||||
elif key in self._ignore:
|
||||
pass
|
||||
elif not _is_descriptor(value):
|
||||
elif isinstance(value, nonmember):
|
||||
# unwrap value here; it won't be processed by the below `else`
|
||||
value = value.value
|
||||
elif _is_descriptor(value):
|
||||
pass
|
||||
# TODO: uncomment next three lines in 3.12
|
||||
# elif _is_internal_class(self._cls_name, value):
|
||||
# # do nothing, name will be a normal attribute
|
||||
# pass
|
||||
else:
|
||||
if key in self:
|
||||
# enum overwriting a descriptor?
|
||||
raise TypeError('%r already defined as %r' % (key, self[key]))
|
||||
elif isinstance(value, member):
|
||||
# unwrap value here -- it will become a member
|
||||
value = value.value
|
||||
if isinstance(value, auto):
|
||||
if value.value == _auto_null:
|
||||
value.value = self._generate_next_value(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue