mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
Issue26988: remove AutoEnum
This commit is contained in:
parent
1cce732fcf
commit
332dbc7325
4 changed files with 77 additions and 663 deletions
133
Lib/enum.py
133
Lib/enum.py
|
@ -8,9 +8,7 @@ except ImportError:
|
|||
from collections import OrderedDict
|
||||
|
||||
|
||||
__all__ = [
|
||||
'EnumMeta', 'Enum', 'IntEnum', 'AutoEnum', 'unique',
|
||||
]
|
||||
__all__ = ['EnumMeta', 'Enum', 'IntEnum', 'unique']
|
||||
|
||||
|
||||
def _is_descriptor(obj):
|
||||
|
@ -54,30 +52,7 @@ class _EnumDict(dict):
|
|||
"""
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# list of enum members
|
||||
self._member_names = []
|
||||
# starting value
|
||||
self._start = None
|
||||
# last assigned value
|
||||
self._last_value = None
|
||||
# when the magic turns off
|
||||
self._locked = True
|
||||
# list of temporary names
|
||||
self._ignore = []
|
||||
|
||||
def __getitem__(self, key):
|
||||
if (
|
||||
self._generate_next_value_ is None
|
||||
or self._locked
|
||||
or key in self
|
||||
or key in self._ignore
|
||||
or _is_sunder(key)
|
||||
or _is_dunder(key)
|
||||
):
|
||||
return super(_EnumDict, self).__getitem__(key)
|
||||
next_value = self._generate_next_value_(key, self._start, len(self._member_names), self._last_value)
|
||||
self[key] = next_value
|
||||
return next_value
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Changes anything not dundered or not a descriptor.
|
||||
|
@ -89,55 +64,19 @@ class _EnumDict(dict):
|
|||
|
||||
"""
|
||||
if _is_sunder(key):
|
||||
if key not in ('_settings_', '_order_', '_ignore_', '_start_', '_generate_next_value_'):
|
||||
raise ValueError('_names_ are reserved for future Enum use')
|
||||
elif key == '_generate_next_value_':
|
||||
if isinstance(value, staticmethod):
|
||||
value = value.__get__(None, self)
|
||||
self._generate_next_value_ = value
|
||||
self._locked = False
|
||||
elif key == '_ignore_':
|
||||
if isinstance(value, str):
|
||||
value = value.split()
|
||||
else:
|
||||
value = list(value)
|
||||
self._ignore = value
|
||||
already = set(value) & set(self._member_names)
|
||||
if already:
|
||||
raise ValueError(
|
||||
'_ignore_ cannot specify already set names: %r'
|
||||
% (already, ))
|
||||
elif key == '_start_':
|
||||
self._start = value
|
||||
self._locked = False
|
||||
raise ValueError('_names_ are reserved for future Enum use')
|
||||
elif _is_dunder(key):
|
||||
if key == '__order__':
|
||||
key = '_order_'
|
||||
if _is_descriptor(value):
|
||||
self._locked = True
|
||||
pass
|
||||
elif key in self._member_names:
|
||||
# descriptor overwriting an enum?
|
||||
raise TypeError('Attempted to reuse key: %r' % key)
|
||||
elif key in self._ignore:
|
||||
pass
|
||||
elif not _is_descriptor(value):
|
||||
if key in self:
|
||||
# enum overwriting a descriptor?
|
||||
raise TypeError('%r already defined as: %r' % (key, self[key]))
|
||||
raise TypeError('Key already defined as: %r' % self[key])
|
||||
self._member_names.append(key)
|
||||
if self._generate_next_value_ is not None:
|
||||
self._last_value = value
|
||||
else:
|
||||
# not a new member, turn off the autoassign magic
|
||||
self._locked = True
|
||||
super().__setitem__(key, value)
|
||||
|
||||
# for magic "auto values" an Enum class should specify a `_generate_next_value_`
|
||||
# method; that method will be used to generate missing values, and is
|
||||
# implicitly a staticmethod;
|
||||
# the signature should be `def _generate_next_value_(name, last_value)`
|
||||
# last_value will be the last value created and/or assigned, or None
|
||||
_generate_next_value_ = None
|
||||
|
||||
|
||||
# Dummy value for Enum as EnumMeta explicitly checks for it, but of course
|
||||
|
@ -145,31 +84,14 @@ class _EnumDict(dict):
|
|||
# This is also why there are checks in EnumMeta like `if Enum is not None`
|
||||
Enum = None
|
||||
|
||||
_ignore_sentinel = object()
|
||||
|
||||
class EnumMeta(type):
|
||||
"""Metaclass for Enum"""
|
||||
@classmethod
|
||||
def __prepare__(metacls, cls, bases, start=None, ignore=_ignore_sentinel):
|
||||
# create the namespace dict
|
||||
enum_dict = _EnumDict()
|
||||
# inherit previous flags and _generate_next_value_ function
|
||||
member_type, first_enum = metacls._get_mixins_(bases)
|
||||
if first_enum is not None:
|
||||
enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None)
|
||||
if start is None:
|
||||
start = getattr(first_enum, '_start_', None)
|
||||
if ignore is _ignore_sentinel:
|
||||
enum_dict['_ignore_'] = 'property classmethod staticmethod'.split()
|
||||
elif ignore:
|
||||
enum_dict['_ignore_'] = ignore
|
||||
if start is not None:
|
||||
enum_dict['_start_'] = start
|
||||
return enum_dict
|
||||
def __prepare__(metacls, cls, bases):
|
||||
return _EnumDict()
|
||||
|
||||
def __init__(cls, *args , **kwds):
|
||||
super(EnumMeta, cls).__init__(*args)
|
||||
|
||||
def __new__(metacls, cls, bases, classdict, **kwds):
|
||||
def __new__(metacls, cls, bases, classdict):
|
||||
# an Enum class is final once enumeration items have been defined; it
|
||||
# cannot be mixed with other types (int, float, etc.) if it has an
|
||||
# inherited __new__ unless a new __new__ is defined (or the resulting
|
||||
|
@ -180,24 +102,12 @@ class EnumMeta(type):
|
|||
|
||||
# save enum items into separate mapping so they don't get baked into
|
||||
# the new class
|
||||
enum_members = {k: classdict[k] for k in classdict._member_names}
|
||||
members = {k: classdict[k] for k in classdict._member_names}
|
||||
for name in classdict._member_names:
|
||||
del classdict[name]
|
||||
|
||||
# adjust the sunders
|
||||
_order_ = classdict.pop('_order_', None)
|
||||
classdict.pop('_ignore_', None)
|
||||
|
||||
# py3 support for definition order (helps keep py2/py3 code in sync)
|
||||
if _order_ is not None:
|
||||
if isinstance(_order_, str):
|
||||
_order_ = _order_.replace(',', ' ').split()
|
||||
unique_members = [n for n in clsdict._member_names if n in _order_]
|
||||
if _order_ != unique_members:
|
||||
raise TypeError('member order does not match _order_')
|
||||
|
||||
# check for illegal enum names (any others?)
|
||||
invalid_names = set(enum_members) & {'mro', }
|
||||
invalid_names = set(members) & {'mro', }
|
||||
if invalid_names:
|
||||
raise ValueError('Invalid enum member name: {0}'.format(
|
||||
','.join(invalid_names)))
|
||||
|
@ -241,7 +151,7 @@ class EnumMeta(type):
|
|||
# a custom __new__ is doing something funky with the values -- such as
|
||||
# auto-numbering ;)
|
||||
for member_name in classdict._member_names:
|
||||
value = enum_members[member_name]
|
||||
value = members[member_name]
|
||||
if not isinstance(value, tuple):
|
||||
args = (value, )
|
||||
else:
|
||||
|
@ -255,10 +165,7 @@ class EnumMeta(type):
|
|||
else:
|
||||
enum_member = __new__(enum_class, *args)
|
||||
if not hasattr(enum_member, '_value_'):
|
||||
if member_type is object:
|
||||
enum_member._value_ = value
|
||||
else:
|
||||
enum_member._value_ = member_type(*args)
|
||||
enum_member._value_ = member_type(*args)
|
||||
value = enum_member._value_
|
||||
enum_member._name_ = member_name
|
||||
enum_member.__objclass__ = enum_class
|
||||
|
@ -665,22 +572,6 @@ class IntEnum(int, Enum):
|
|||
def _reduce_ex_by_name(self, proto):
|
||||
return self.name
|
||||
|
||||
class AutoEnum(Enum):
|
||||
"""Enum where values are automatically assigned."""
|
||||
def _generate_next_value_(name, start, count, last_value):
|
||||
"""
|
||||
Generate the next value when not given.
|
||||
|
||||
name: the name of the member
|
||||
start: the initital start value or None
|
||||
count: the number of existing members
|
||||
last_value: the last value assigned or None
|
||||
"""
|
||||
# add one to the last assigned value
|
||||
if not count:
|
||||
return start if start is not None else 1
|
||||
return last_value + 1
|
||||
|
||||
def unique(enumeration):
|
||||
"""Class decorator for enumerations ensuring unique member values."""
|
||||
duplicates = []
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue