[Enum] reformat and add doc strings (GH-23705)
This commit is contained in:
parent
37440eef7f
commit
6d3dfee271
185
Lib/enum.py
185
Lib/enum.py
|
@ -10,31 +10,41 @@ __all__ = [
|
|||
|
||||
|
||||
def _is_descriptor(obj):
|
||||
"""Returns True if obj is a descriptor, False otherwise."""
|
||||
"""
|
||||
Returns True if obj is a descriptor, False otherwise.
|
||||
"""
|
||||
return (
|
||||
hasattr(obj, '__get__') or
|
||||
hasattr(obj, '__set__') or
|
||||
hasattr(obj, '__delete__'))
|
||||
|
||||
hasattr(obj, '__delete__')
|
||||
)
|
||||
|
||||
def _is_dunder(name):
|
||||
"""Returns True if a __dunder__ name, False otherwise."""
|
||||
return (len(name) > 4 and
|
||||
"""
|
||||
Returns True if a __dunder__ name, False otherwise.
|
||||
"""
|
||||
return (
|
||||
len(name) > 4 and
|
||||
name[:2] == name[-2:] == '__' and
|
||||
name[2] != '_' and
|
||||
name[-3] != '_')
|
||||
|
||||
name[-3] != '_'
|
||||
)
|
||||
|
||||
def _is_sunder(name):
|
||||
"""Returns True if a _sunder_ name, False otherwise."""
|
||||
return (len(name) > 2 and
|
||||
"""
|
||||
Returns True if a _sunder_ name, False otherwise.
|
||||
"""
|
||||
return (
|
||||
len(name) > 2 and
|
||||
name[0] == name[-1] == '_' and
|
||||
name[1:2] != '_' and
|
||||
name[-2:-1] != '_')
|
||||
|
||||
name[-2:-1] != '_'
|
||||
)
|
||||
|
||||
def _make_class_unpicklable(cls):
|
||||
"""Make the given class un-picklable."""
|
||||
"""
|
||||
Make the given class un-picklable.
|
||||
"""
|
||||
def _break_on_call_reduce(self, proto):
|
||||
raise TypeError('%r cannot be pickled' % self)
|
||||
cls.__reduce_ex__ = _break_on_call_reduce
|
||||
|
@ -49,11 +59,11 @@ class auto:
|
|||
|
||||
|
||||
class _EnumDict(dict):
|
||||
"""Track enum member order and ensure member names are not reused.
|
||||
"""
|
||||
Track enum member order and ensure member names are not reused.
|
||||
|
||||
EnumMeta will use the names found in self._member_names as the
|
||||
enumeration member names.
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
@ -63,21 +73,23 @@ class _EnumDict(dict):
|
|||
self._auto_called = False
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Changes anything not dundered or not a descriptor.
|
||||
"""
|
||||
Changes anything not dundered or not a descriptor.
|
||||
|
||||
If an enum member name is used twice, an error is raised; duplicate
|
||||
values are not checked for.
|
||||
|
||||
Single underscore (sunder) names are reserved.
|
||||
|
||||
"""
|
||||
if _is_sunder(key):
|
||||
if key not in (
|
||||
'_order_', '_create_pseudo_member_',
|
||||
'_generate_next_value_', '_missing_', '_ignore_',
|
||||
):
|
||||
raise ValueError(f'_sunder_ names, such as "{key}", are '
|
||||
'reserved for future Enum use')
|
||||
raise ValueError(
|
||||
'_sunder_ names, such as %r, are reserved for future Enum use'
|
||||
% (key, )
|
||||
)
|
||||
if key == '_generate_next_value_':
|
||||
# check if members already defined as auto()
|
||||
if self._auto_called:
|
||||
|
@ -91,7 +103,10 @@ class _EnumDict(dict):
|
|||
self._ignore = value
|
||||
already = set(value) & set(self._member_names)
|
||||
if already:
|
||||
raise ValueError('_ignore_ cannot specify already set names: %r' % (already, ))
|
||||
raise ValueError(
|
||||
'_ignore_ cannot specify already set names: %r'
|
||||
% (already, )
|
||||
)
|
||||
elif _is_dunder(key):
|
||||
if key == '__order__':
|
||||
key = '_order_'
|
||||
|
@ -106,7 +121,12 @@ class _EnumDict(dict):
|
|||
raise TypeError('%r already defined as: %r' % (key, self[key]))
|
||||
if isinstance(value, auto):
|
||||
if value.value == _auto_null:
|
||||
value.value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:])
|
||||
value.value = self._generate_next_value(
|
||||
key,
|
||||
1,
|
||||
len(self._member_names),
|
||||
self._last_values[:],
|
||||
)
|
||||
self._auto_called = True
|
||||
value = value.value
|
||||
self._member_names.append(key)
|
||||
|
@ -119,9 +139,10 @@ class _EnumDict(dict):
|
|||
# This is also why there are checks in EnumMeta like `if Enum is not None`
|
||||
Enum = None
|
||||
|
||||
|
||||
class EnumMeta(type):
|
||||
"""Metaclass for Enum"""
|
||||
"""
|
||||
Metaclass for Enum
|
||||
"""
|
||||
@classmethod
|
||||
def __prepare__(metacls, cls, bases):
|
||||
# check that previous enum members do not exist
|
||||
|
@ -131,7 +152,9 @@ class EnumMeta(type):
|
|||
# inherit previous flags and _generate_next_value_ function
|
||||
member_type, first_enum = metacls._get_mixins_(cls, bases)
|
||||
if first_enum is not None:
|
||||
enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None)
|
||||
enum_dict['_generate_next_value_'] = getattr(
|
||||
first_enum, '_generate_next_value_', None,
|
||||
)
|
||||
return enum_dict
|
||||
|
||||
def __new__(metacls, cls, bases, classdict):
|
||||
|
@ -177,9 +200,11 @@ class EnumMeta(type):
|
|||
|
||||
# save DynamicClassAttribute attributes from super classes so we know
|
||||
# if we can take the shortcut of storing members in the class dict
|
||||
dynamic_attributes = {k for c in enum_class.mro()
|
||||
dynamic_attributes = {
|
||||
k for c in enum_class.mro()
|
||||
for k, v in c.__dict__.items()
|
||||
if isinstance(v, DynamicClassAttribute)}
|
||||
if isinstance(v, DynamicClassAttribute)
|
||||
}
|
||||
|
||||
# Reverse value->name map for hashable values.
|
||||
enum_class._value2member_map_ = {}
|
||||
|
@ -289,7 +314,8 @@ class EnumMeta(type):
|
|||
return True
|
||||
|
||||
def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
|
||||
"""Either returns an existing member, or creates a new enum class.
|
||||
"""
|
||||
Either returns an existing member, or creates a new enum class.
|
||||
|
||||
This method is used both when an enum class is given a value to match
|
||||
to an enumeration member (i.e. Color(3)) and for the functional API
|
||||
|
@ -311,12 +337,18 @@ class EnumMeta(type):
|
|||
not correct, unpickling will fail in some circumstances.
|
||||
|
||||
`type`, if set, will be mixed in as the first base class.
|
||||
|
||||
"""
|
||||
if names is None: # simple value lookup
|
||||
return cls.__new__(cls, value)
|
||||
# otherwise, functional API: we're creating a new Enum type
|
||||
return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
|
||||
return cls._create_(
|
||||
value,
|
||||
names,
|
||||
module=module,
|
||||
qualname=qualname,
|
||||
type=type,
|
||||
start=start,
|
||||
)
|
||||
|
||||
def __contains__(cls, member):
|
||||
if not isinstance(member, Enum):
|
||||
|
@ -329,22 +361,23 @@ class EnumMeta(type):
|
|||
# nicer error message when someone tries to delete an attribute
|
||||
# (see issue19025).
|
||||
if attr in cls._member_map_:
|
||||
raise AttributeError(
|
||||
"%s: cannot delete Enum member." % cls.__name__)
|
||||
raise AttributeError("%s: cannot delete Enum member %r." % (cls.__name__, attr))
|
||||
super().__delattr__(attr)
|
||||
|
||||
def __dir__(self):
|
||||
return (['__class__', '__doc__', '__members__', '__module__'] +
|
||||
self._member_names_)
|
||||
return (
|
||||
['__class__', '__doc__', '__members__', '__module__']
|
||||
+ self._member_names_
|
||||
)
|
||||
|
||||
def __getattr__(cls, name):
|
||||
"""Return the enum member matching `name`
|
||||
"""
|
||||
Return the enum member matching `name`
|
||||
|
||||
We use __getattr__ instead of descriptors or inserting into the enum
|
||||
class' __dict__ in order to support `name` and `value` being both
|
||||
properties for enum members (which live in the class' __dict__) and
|
||||
enum members themselves.
|
||||
|
||||
"""
|
||||
if _is_dunder(name):
|
||||
raise AttributeError(name)
|
||||
|
@ -357,6 +390,9 @@ class EnumMeta(type):
|
|||
return cls._member_map_[name]
|
||||
|
||||
def __iter__(cls):
|
||||
"""
|
||||
Returns members in definition order.
|
||||
"""
|
||||
return (cls._member_map_[name] for name in cls._member_names_)
|
||||
|
||||
def __len__(cls):
|
||||
|
@ -364,11 +400,11 @@ class EnumMeta(type):
|
|||
|
||||
@property
|
||||
def __members__(cls):
|
||||
"""Returns a mapping of member name->value.
|
||||
"""
|
||||
Returns a mapping of member name->value.
|
||||
|
||||
This mapping lists all enum members, including aliases. Note that this
|
||||
is a read-only view of the internal mapping.
|
||||
|
||||
"""
|
||||
return MappingProxyType(cls._member_map_)
|
||||
|
||||
|
@ -376,15 +412,18 @@ class EnumMeta(type):
|
|||
return "<enum %r>" % cls.__name__
|
||||
|
||||
def __reversed__(cls):
|
||||
"""
|
||||
Returns members in reverse definition order.
|
||||
"""
|
||||
return (cls._member_map_[name] for name in reversed(cls._member_names_))
|
||||
|
||||
def __setattr__(cls, name, value):
|
||||
"""Block attempts to reassign Enum members.
|
||||
"""
|
||||
Block attempts to reassign Enum members.
|
||||
|
||||
A simple assignment to the class namespace only changes one of the
|
||||
several possible ways to get an Enum member from the Enum class,
|
||||
resulting in an inconsistent Enumeration.
|
||||
|
||||
"""
|
||||
member_map = cls.__dict__.get('_member_map_', {})
|
||||
if name in member_map:
|
||||
|
@ -392,7 +431,8 @@ class EnumMeta(type):
|
|||
super().__setattr__(name, value)
|
||||
|
||||
def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, start=1):
|
||||
"""Convenience method to create a new Enum class.
|
||||
"""
|
||||
Convenience method to create a new Enum class.
|
||||
|
||||
`names` can be:
|
||||
|
||||
|
@ -401,7 +441,6 @@ class EnumMeta(type):
|
|||
* An iterable of member names. Values are incremented by 1 from `start`.
|
||||
* An iterable of (member name, value) pairs.
|
||||
* A mapping of member name -> value pairs.
|
||||
|
||||
"""
|
||||
metacls = cls.__class__
|
||||
bases = (cls, ) if type is None else (type, cls)
|
||||
|
@ -482,15 +521,18 @@ class EnumMeta(type):
|
|||
for chain in bases:
|
||||
for base in chain.__mro__:
|
||||
if issubclass(base, Enum) and base._member_names_:
|
||||
raise TypeError("%s: cannot extend enumeration %r" % (class_name, base.__name__))
|
||||
raise TypeError(
|
||||
"%s: cannot extend enumeration %r"
|
||||
% (class_name, base.__name__)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_mixins_(class_name, bases):
|
||||
"""Returns the type for creating enum members, and the first inherited
|
||||
"""
|
||||
Returns the type for creating enum members, and the first inherited
|
||||
enum class.
|
||||
|
||||
bases: the tuple of bases that was given to __new__
|
||||
|
||||
"""
|
||||
if not bases:
|
||||
return object, Enum
|
||||
|
@ -533,12 +575,12 @@ class EnumMeta(type):
|
|||
|
||||
@staticmethod
|
||||
def _find_new_(classdict, member_type, first_enum):
|
||||
"""Returns the __new__ to be used for creating the enum members.
|
||||
"""
|
||||
Returns the __new__ to be used for creating the enum members.
|
||||
|
||||
classdict: the class dictionary given to __new__
|
||||
member_type: the data type whose __new__ will be used by default
|
||||
first_enum: enumeration to check for an overriding __new__
|
||||
|
||||
"""
|
||||
# now find the correct __new__, checking to see of one was defined
|
||||
# by the user; also check earlier enum classes in case a __new__ was
|
||||
|
@ -578,10 +620,10 @@ class EnumMeta(type):
|
|||
|
||||
|
||||
class Enum(metaclass=EnumMeta):
|
||||
"""Generic enumeration.
|
||||
"""
|
||||
Generic enumeration.
|
||||
|
||||
Derive from this class to define new enumerations.
|
||||
|
||||
"""
|
||||
def __new__(cls, value):
|
||||
# all enum instances are actually created during class construction
|
||||
|
@ -624,6 +666,14 @@ class Enum(metaclass=EnumMeta):
|
|||
raise exc
|
||||
|
||||
def _generate_next_value_(name, start, count, last_values):
|
||||
"""
|
||||
Generate the next value when not given.
|
||||
|
||||
name: the name of the member
|
||||
start: the initial start value or None
|
||||
count: the number of existing members
|
||||
last_value: the last value assigned or None
|
||||
"""
|
||||
for last_value in reversed(last_values):
|
||||
try:
|
||||
return last_value + 1
|
||||
|
@ -644,6 +694,9 @@ class Enum(metaclass=EnumMeta):
|
|||
return "%s.%s" % (self.__class__.__name__, self._name_)
|
||||
|
||||
def __dir__(self):
|
||||
"""
|
||||
Returns all members and all public methods
|
||||
"""
|
||||
added_behavior = [
|
||||
m
|
||||
for cls in self.__class__.mro()
|
||||
|
@ -653,6 +706,9 @@ class Enum(metaclass=EnumMeta):
|
|||
return (['__class__', '__doc__', '__module__'] + added_behavior)
|
||||
|
||||
def __format__(self, format_spec):
|
||||
"""
|
||||
Returns format using actual value type unless __str__ has been overridden.
|
||||
"""
|
||||
# mixed-in Enums should use the mixed-in type's __format__, otherwise
|
||||
# we can get strange results with the Enum name showing up instead of
|
||||
# the value
|
||||
|
@ -730,7 +786,9 @@ def _reduce_ex_by_name(self, proto):
|
|||
return self.name
|
||||
|
||||
class Flag(Enum):
|
||||
"""Support for flags"""
|
||||
"""
|
||||
Support for flags
|
||||
"""
|
||||
|
||||
def _generate_next_value_(name, start, count, last_values):
|
||||
"""
|
||||
|
@ -753,6 +811,9 @@ class Flag(Enum):
|
|||
|
||||
@classmethod
|
||||
def _missing_(cls, value):
|
||||
"""
|
||||
Returns member (possibly creating it) if one can be found for value.
|
||||
"""
|
||||
original_value = value
|
||||
if value < 0:
|
||||
value = ~value
|
||||
|
@ -782,6 +843,9 @@ class Flag(Enum):
|
|||
return pseudo_member
|
||||
|
||||
def __contains__(self, other):
|
||||
"""
|
||||
Returns True if self has at least the same flags set as other.
|
||||
"""
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError(
|
||||
"unsupported operand type(s) for 'in': '%s' and '%s'" % (
|
||||
|
@ -789,6 +853,9 @@ class Flag(Enum):
|
|||
return other._value_ & self._value_ == other._value_
|
||||
|
||||
def __iter__(self):
|
||||
"""
|
||||
Returns flags in decreasing value order.
|
||||
"""
|
||||
members, extra_flags = _decompose(self.__class__, self.value)
|
||||
return (m for m in members if m._value_ != 0)
|
||||
|
||||
|
@ -844,10 +911,15 @@ class Flag(Enum):
|
|||
|
||||
|
||||
class IntFlag(int, Flag):
|
||||
"""Support for integer-based Flags"""
|
||||
"""
|
||||
Support for integer-based Flags
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def _missing_(cls, value):
|
||||
"""
|
||||
Returns member (possibly creating it) if one can be found for value.
|
||||
"""
|
||||
if not isinstance(value, int):
|
||||
raise ValueError("%r is not a valid %s" % (value, cls.__qualname__))
|
||||
new_member = cls._create_pseudo_member_(value)
|
||||
|
@ -855,6 +927,9 @@ class IntFlag(int, Flag):
|
|||
|
||||
@classmethod
|
||||
def _create_pseudo_member_(cls, value):
|
||||
"""
|
||||
Create a composite member iff value contains only members.
|
||||
"""
|
||||
pseudo_member = cls._value2member_map_.get(value, None)
|
||||
if pseudo_member is None:
|
||||
need_to_create = [value]
|
||||
|
@ -909,11 +984,15 @@ class IntFlag(int, Flag):
|
|||
|
||||
|
||||
def _high_bit(value):
|
||||
"""returns index of highest bit, or -1 if value is zero or negative"""
|
||||
"""
|
||||
returns index of highest bit, or -1 if value is zero or negative
|
||||
"""
|
||||
return value.bit_length() - 1
|
||||
|
||||
def unique(enumeration):
|
||||
"""Class decorator for enumerations ensuring unique member values."""
|
||||
"""
|
||||
Class decorator for enumerations ensuring unique member values.
|
||||
"""
|
||||
duplicates = []
|
||||
for name, member in enumeration.__members__.items():
|
||||
if name != member.name:
|
||||
|
@ -926,7 +1005,9 @@ def unique(enumeration):
|
|||
return enumeration
|
||||
|
||||
def _decompose(flag, value):
|
||||
"""Extract all members from the value."""
|
||||
"""
|
||||
Extract all members from the value.
|
||||
"""
|
||||
# _decompose is only called if the value is not named
|
||||
not_covered = value
|
||||
negative = value < 0
|
||||
|
|
|
@ -429,7 +429,7 @@ class TestEnum(unittest.TestCase):
|
|||
def test_reserved__sunder_(self):
|
||||
with self.assertRaisesRegex(
|
||||
ValueError,
|
||||
'_sunder_ names, such as "_bad_", are reserved',
|
||||
"_sunder_ names, such as '_bad_', are reserved",
|
||||
):
|
||||
class Bad(Enum):
|
||||
_bad_ = 1
|
||||
|
|
Loading…
Reference in New Issue