Thanks to visit codestin.com
Credit goes to github.com

Skip to content

bpo-33976: support nested classes in Enum #7950

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Doc/library/enum.rst
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ return A::
.. note::

Attempting to create a member with the same name as an already
defined attribute (another member, a method, etc.) or attempting to create
defined attribute (another member, a method, a class, etc.) or attempting to create
an attribute with the same name as a member is not allowed.


Expand Down
36 changes: 32 additions & 4 deletions Lib/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,32 @@ def __setitem__(self, key, value):
raise TypeError('Attempted to reuse key: %r' % key)
elif key in self._ignore:
pass
elif not _is_descriptor(value):
elif _is_descriptor(value):
# Don't treat methods, etc as enum values.
pass
else:
if key in self:
# enum overwriting a descriptor?
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.value
self._member_names.append(key)
self._last_values.append(value)
self.add_member(key, value)
super().__setitem__(key, value)

def add_member(self, key, value):
"""Add a member by key and value."""
self._member_names.append(key)
self._last_values.append(value)

def remove_member(self, key):
"""Remove a member (reverses add_member() above) by key, if present."""
if key in self._member_names:
index = self._member_names.index(key)
del self._member_names[index]
del self._last_values[index]


# 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.
Expand All @@ -130,7 +144,21 @@ def __new__(metacls, cls, bases, classdict):
# cannot be mixed with other types (int, float, etc.) if it has an
# inherited __new__ unless a new __new__ is defined (or the resulting
# class will fail).
#

# Get __qualname__ for the class being created.
enum_class_qualname = super().__new__(metacls, cls, bases, classdict).__qualname__

# We want to avoid treating locally-defined nested classes as enum
# values, so we use __qualname__ to determine this.
# e.g. if a class Bar is defined inside an enum Foo, then say if
# enum_class.__qualname__ is Foo, then member.__qualname__ will be Bar.
# We have to do it here since __qualname__ of the new Enum isn't
# accessible in _EnumDict.__setitem__().
for key, member in classdict.items():
if isinstance(member, type):
if member.__qualname__.startswith(enum_class_qualname + "."):
classdict.remove_member(key)

# remove any keys listed in _ignore_
classdict.setdefault('_ignore_', []).append('_ignore_')
ignore = classdict['_ignore_']
Expand Down
43 changes: 43 additions & 0 deletions Lib/test/test_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,49 @@ def red(self):
green = 2
blue = 3

def test_enum_of_types(self):
"""Support using Enum to refer to types deliberately."""
class MyTypes(Enum):
i = int
f = float
s = str
self.assertEqual(MyTypes.i.value, int)
self.assertEqual(MyTypes.f.value, float)
self.assertEqual(MyTypes.s.value, str)
class Foo:
pass
class Bar:
pass
class MyTypes2(Enum):
a = Foo
b = Bar
self.assertEqual(MyTypes2.a.value, Foo)
self.assertEqual(MyTypes2.b.value, Bar)
class SpamEnumNotInner:
pass
class SpamEnum(Enum):
spam = SpamEnumNotInner
self.assertEqual(SpamEnum.spam.value, SpamEnumNotInner)

def test_nested_classes_in_enum(self):
"""Support locally-defined nested classes."""
class Outer(Enum):
a = 1
b = 2
class Inner(Enum):
foo = 10
bar = 11
self.assertTrue(isinstance(Outer.Inner, type))
self.assertEqual(Outer.a.value, 1)
self.assertEqual(Outer.Inner.foo.value, 10)
self.assertEqual(
list(Outer.Inner),
[Outer.Inner.foo, Outer.Inner.bar],
)
self.assertEqual(
list(Outer),
[Outer.a, Outer.b],
)

def test_enum_with_value_name(self):
class Huh(Enum):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support nested classes in Enum. Patch by Edward Wang.