bpo-34750: [Enum] add `_EnumDict.update()` support (GH-23725)

This allows easier Enum construction in unusual cases, such as including dynamic member definitions into a class definition:

# created dynamically
foo_defines = {'FOO_CAT': 'aloof', 'BAR_DOG': 'friendly', 'FOO_HORSE': 'big'}

class Foo(Enum):
    vars().update({
            k: v
            for k, v in foo_defines.items()
            if k.startswith('FOO_')
            })
    def upper(self):
        # example method
        return self.value.upper()
This commit is contained in:
Ethan Furman 2020-12-10 13:07:00 -08:00 committed by GitHub
parent efb13be72c
commit a658287179
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 1 deletions

View File

@ -136,7 +136,7 @@ class _EnumDict(dict):
key = '_order_' key = '_order_'
elif key in self._member_names: elif key in self._member_names:
# descriptor overwriting an enum? # descriptor overwriting an enum?
raise TypeError('Attempted to reuse key: %r' % key) raise TypeError('%r already defined as: %r' % (key, self[key]))
elif key in self._ignore: elif key in self._ignore:
pass pass
elif not _is_descriptor(value): elif not _is_descriptor(value):
@ -157,6 +157,16 @@ class _EnumDict(dict):
self._last_values.append(value) self._last_values.append(value)
super().__setitem__(key, value) super().__setitem__(key, value)
def update(self, members, **more_members):
try:
for name in members.keys():
self[name] = members[name]
except AttributeError:
for name, value in members:
self[name] = value
for name, value in more_members.items():
self[name] = value
# Dummy value for Enum as EnumMeta explicitly checks for it, but of course # 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. # until EnumMeta finishes running the first time the Enum class doesn't exist.

View File

@ -2186,6 +2186,34 @@ class TestEnum(unittest.TestCase):
self.assertEqual([Strings.ONE, Strings.TWO], ['one', 'two']) self.assertEqual([Strings.ONE, Strings.TWO], ['one', 'two'])
def test_dynamic_members_with_static_methods(self):
#
foo_defines = {'FOO_CAT': 'aloof', 'BAR_DOG': 'friendly', 'FOO_HORSE': 'big'}
class Foo(Enum):
vars().update({
k: v
for k, v in foo_defines.items()
if k.startswith('FOO_')
})
def upper(self):
return self.value.upper()
self.assertEqual(list(Foo), [Foo.FOO_CAT, Foo.FOO_HORSE])
self.assertEqual(Foo.FOO_CAT.value, 'aloof')
self.assertEqual(Foo.FOO_HORSE.upper(), 'BIG')
#
with self.assertRaisesRegex(TypeError, "'FOO_CAT' already defined as: 'aloof'"):
class FooBar(Enum):
vars().update({
k: v
for k, v in foo_defines.items()
if k.startswith('FOO_')
},
**{'FOO_CAT': 'small'},
)
def upper(self):
return self.value.upper()
class TestOrder(unittest.TestCase): class TestOrder(unittest.TestCase):
def test_same_members(self): def test_same_members(self):

View File

@ -0,0 +1 @@
[Enum] `_EnumDict.update()` is now supported