summaryrefslogtreecommitdiff
path: root/Lib/enum.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/enum.py')
-rw-r--r--Lib/enum.py69
1 files changed, 42 insertions, 27 deletions
diff --git a/Lib/enum.py b/Lib/enum.py
index 82058ae924..86f94d905f 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -16,6 +16,8 @@ class _RouteClassAttributeToGetattr:
"""
def __init__(self, fget=None):
self.fget = fget
+ if fget.__doc__ is not None:
+ self.__doc__ = fget.__doc__
def __get__(self, instance, ownerclass=None):
if instance is None:
@@ -29,6 +31,14 @@ class _RouteClassAttributeToGetattr:
raise AttributeError("can't delete attribute")
+def _is_descriptor(obj):
+ """Returns True if obj is a descriptor, False otherwise."""
+ return (
+ hasattr(obj, '__get__') or
+ hasattr(obj, '__set__') or
+ hasattr(obj, '__delete__'))
+
+
def _is_dunder(name):
"""Returns True if a __dunder__ name, False otherwise."""
return (name[:2] == name[-2:] == '__' and
@@ -50,8 +60,9 @@ def _make_class_unpicklable(cls):
cls.__reduce__ = _break_on_call_reduce
cls.__module__ = '<unknown>'
+
class _EnumDict(dict):
- """Keeps track of definition order of the enum items.
+ """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.
@@ -62,11 +73,7 @@ class _EnumDict(dict):
self._member_names = []
def __setitem__(self, key, value):
- """Changes anything not dundered or that doesn't have __get__.
-
- If a descriptor is added with the same name as an enum member, the name
- is removed from _member_names (this may leave a hole in the numerical
- sequence of values).
+ """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.
@@ -76,19 +83,20 @@ class _EnumDict(dict):
"""
if _is_sunder(key):
raise ValueError('_names_ are reserved for future Enum use')
- elif _is_dunder(key) or hasattr(value, '__get__'):
- if key in self._member_names:
- # overwriting an enum with a method? then remove the name from
- # _member_names or it will become an enum anyway when the class
- # is created
- self._member_names.remove(key)
- else:
- if key in self._member_names:
- raise TypeError('Attempted to reuse key: %r' % key)
+ elif _is_dunder(key):
+ pass
+ elif key in self._member_names:
+ # descriptor overwriting an enum?
+ raise TypeError('Attempted to reuse key: %r' % key)
+ elif not _is_descriptor(value):
+ if key in self:
+ # enum overwriting a descriptor?
+ raise TypeError('Key already defined as: %r' % self[key])
self._member_names.append(key)
super().__setitem__(key, value)
+
# Dummy value for Enum as EnumMeta explicitly checks for it, but of course
# until EnumMeta finishes running the first time the Enum class doesn't exist.
# This is also why there are checks in EnumMeta like `if Enum is not None`
@@ -160,6 +168,7 @@ class EnumMeta(type):
enum_member._value_ = member_type(*args)
value = enum_member._value_
enum_member._name_ = member_name
+ enum_member.__objclass__ = enum_class
enum_member.__init__(*args)
# If another member with the same value was already defined, the
# new member becomes an alias to the existing one.
@@ -223,17 +232,7 @@ class EnumMeta(type):
return isinstance(member, cls) and member.name in cls._member_map_
def __dir__(self):
- return ['__class__', '__doc__', '__members__'] + self._member_names_
-
- @property
- def __members__(cls):
- """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_)
+ return ['__class__', '__doc__', '__members__', '__module__'] + self._member_names_
def __getattr__(cls, name):
"""Return the enum member matching `name`
@@ -260,9 +259,22 @@ class EnumMeta(type):
def __len__(cls):
return len(cls._member_names_)
+ @property
+ def __members__(cls):
+ """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_)
+
def __repr__(cls):
return "<enum %r>" % cls.__name__
+ def __reversed__(cls):
+ return (cls._member_map_[name] for name in reversed(cls._member_names_))
+
def __setattr__(cls, name, value):
"""Block attempts to reassign Enum members.
@@ -446,7 +458,8 @@ class Enum(metaclass=EnumMeta):
return "%s.%s" % (self.__class__.__name__, self._name_)
def __dir__(self):
- return (['__class__', '__doc__', 'name', 'value'])
+ added_behavior = [m for m in self.__class__.__dict__ if m[0] != '_']
+ return ['__class__', '__doc__', '__module__', 'name', 'value'] + added_behavior
def __eq__(self, other):
if type(other) is self.__class__:
@@ -483,10 +496,12 @@ class Enum(metaclass=EnumMeta):
@_RouteClassAttributeToGetattr
def name(self):
+ """The name of the Enum member."""
return self._name_
@_RouteClassAttributeToGetattr
def value(self):
+ """The value of the Enum member."""
return self._value_