diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2022-05-06 00:16:22 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-06 00:16:22 -0700 |
commit | 93364f9716614173406a4c83cd624b37d9a02ebf (patch) | |
tree | 21604d3f0b29c347f14fe3e6a1c79bf60e61b687 /Lib/enum.py | |
parent | fa4f0a134e7911b2494ea9866c8a49ff446f9d6c (diff) | |
download | cpython-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.py | 57 |
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( |