Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
101e0746
Commit
101e0746
authored
Sep 15, 2013
by
Ethan Furman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Close #18989: enum members will no longer overwrite other attributes, nor be overwritten by them.
parent
defe7f4c
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
53 additions
and
26 deletions
+53
-26
Doc/library/enum.rst
Doc/library/enum.rst
+6
-0
Lib/enum.py
Lib/enum.py
+21
-15
Lib/test/test_enum.py
Lib/test/test_enum.py
+26
-11
No files found.
Doc/library/enum.rst
View file @
101e0746
...
...
@@ -154,6 +154,12 @@ return A::
>>> Shape(2)
<Shape.square: 2>
.. note::
Attempting to create a member with the same name as an already
defined attribute (another member, a method, etc.) or attempting to create
an attribute with the same name as a member is not allowed.
Ensuring unique enumeration values
----------------------------------
...
...
Lib/enum.py
View file @
101e0746
...
...
@@ -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`
...
...
Lib/test/test_enum.py
View file @
101e0746
...
...
@@ -228,6 +228,32 @@ class TestEnum(unittest.TestCase):
[
'FALL'
,
'ANOTHER_SPRING'
],
)
def
test_duplicate_name
(
self
):
with
self
.
assertRaises
(
TypeError
):
class
Color
(
Enum
):
red
=
1
green
=
2
blue
=
3
red
=
4
with
self
.
assertRaises
(
TypeError
):
class
Color
(
Enum
):
red
=
1
green
=
2
blue
=
3
def
red
(
self
):
return
'red'
with
self
.
assertRaises
(
TypeError
):
class
Color
(
Enum
):
@
property
def
red
(
self
):
return
'redder'
red
=
1
green
=
2
blue
=
3
def
test_enum_with_value_name
(
self
):
class
Huh
(
Enum
):
name
=
1
...
...
@@ -618,17 +644,6 @@ class TestEnum(unittest.TestCase):
self
.
assertIsNot
(
type
(
whatever
.
really
),
whatever
)
self
.
assertEqual
(
whatever
.
this
.
really
(),
'no, not that'
)
def
test_overwrite_enums
(
self
):
class
Why
(
Enum
):
question
=
1
answer
=
2
propisition
=
3
def
question
(
self
):
print
(
42
)
self
.
assertIsNot
(
type
(
Why
.
question
),
Why
)
self
.
assertNotIn
(
Why
.
question
,
Why
.
_member_names_
)
self
.
assertNotIn
(
Why
.
question
,
Why
)
def
test_wrong_inheritance_order
(
self
):
with
self
.
assertRaises
(
TypeError
):
class
Wrong
(
Enum
,
str
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment