aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/enum.py
diff options
context:
space:
mode:
authorEthan Furman <ethan@stoneleaf.us>2022-05-06 00:16:22 -0700
committerGitHub <noreply@github.com>2022-05-06 00:16:22 -0700
commit93364f9716614173406a4c83cd624b37d9a02ebf (patch)
tree21604d3f0b29c347f14fe3e6a1c79bf60e61b687 /Lib/enum.py
parentfa4f0a134e7911b2494ea9866c8a49ff446f9d6c (diff)
downloadcpython-93364f9716614173406a4c83cd624b37d9a02ebf.tar.gz
cpython-93364f9716614173406a4c83cd624b37d9a02ebf.zip
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
Diffstat (limited to 'Lib/enum.py')
-rw-r--r--Lib/enum.py57
1 files changed, 53 insertions, 4 deletions
diff --git a/Lib/enum.py b/Lib/enum.py
index 85245c95f9a..b9811fe9e67 100644
--- a/Lib/enum.py
+++ b/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(