diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2013-09-15 19:34:36 (GMT) |
---|---|---|
committer | Ethan Furman <ethan@stoneleaf.us> | 2013-09-15 19:34:36 (GMT) |
commit | 101e0746d36caa56b93d5c61d8e54938c6edf93b (patch) | |
tree | 06f884e13cf1880a0f3a003bc4bb60b0c76a91b3 /Lib/enum.py | |
parent | defe7f4c62b992380fdda2833bb46a6505c839f4 (diff) | |
download | cpython-101e0746d36caa56b93d5c61d8e54938c6edf93b.zip cpython-101e0746d36caa56b93d5c61d8e54938c6edf93b.tar.gz cpython-101e0746d36caa56b93d5c61d8e54938c6edf93b.tar.bz2 |
Close #18989: enum members will no longer overwrite other attributes, nor be overwritten by them.
Diffstat (limited to 'Lib/enum.py')
-rw-r--r-- | Lib/enum.py | 36 |
1 files changed, 21 insertions, 15 deletions
diff --git a/Lib/enum.py b/Lib/enum.py index 0d72f3e..40546da 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -29,6 +29,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 +58,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 +71,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 +81,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` |