mirror of
https://github.com/python/cpython.git
synced 2025-12-23 09:19:18 +00:00
bpo-45535: [Enum] include special dunders in dir() (GH-30677)
Include the `__dunders__` in `dir()` that make `Enum` special: - `__contains__` - `__getitem__` - `__iter__` - `__len__` - `__members__`
This commit is contained in:
parent
3852269b91
commit
7c0914d35e
2 changed files with 42 additions and 44 deletions
33
Lib/enum.py
33
Lib/enum.py
|
|
@ -766,29 +766,22 @@ class EnumType(type):
|
|||
super().__delattr__(attr)
|
||||
|
||||
def __dir__(cls):
|
||||
# TODO: check for custom __init__, __new__, __format__, __repr__, __str__, __init_subclass__
|
||||
# on object-based enums
|
||||
interesting = set([
|
||||
'__class__', '__contains__', '__doc__', '__getitem__',
|
||||
'__iter__', '__len__', '__members__', '__module__',
|
||||
'__name__', '__qualname__',
|
||||
]
|
||||
+ cls._member_names_
|
||||
)
|
||||
if cls._new_member_ is not object.__new__:
|
||||
interesting.add('__new__')
|
||||
if cls.__init_subclass__ is not object.__init_subclass__:
|
||||
interesting.add('__init_subclass__')
|
||||
if cls._member_type_ is object:
|
||||
interesting = set(cls._member_names_)
|
||||
if cls._new_member_ is not object.__new__:
|
||||
interesting.add('__new__')
|
||||
if cls.__init_subclass__ is not object.__init_subclass__:
|
||||
interesting.add('__init_subclass__')
|
||||
for method in ('__init__', '__format__', '__repr__', '__str__'):
|
||||
if getattr(cls, method) not in (getattr(Enum, method), getattr(Flag, method)):
|
||||
interesting.add(method)
|
||||
return sorted(set([
|
||||
'__class__', '__contains__', '__doc__', '__getitem__',
|
||||
'__iter__', '__len__', '__members__', '__module__',
|
||||
'__name__', '__qualname__',
|
||||
]) | interesting
|
||||
)
|
||||
return sorted(interesting)
|
||||
else:
|
||||
# return whatever mixed-in data type has
|
||||
return sorted(set(
|
||||
dir(cls._member_type_)
|
||||
+ cls._member_names_
|
||||
))
|
||||
return sorted(set(dir(cls._member_type_)) | interesting)
|
||||
|
||||
def __getattr__(cls, name):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -883,14 +883,15 @@ class TestSpecial(unittest.TestCase):
|
|||
with self.assertRaises(TypeError):
|
||||
Season.SPRING < Part.CLIP
|
||||
|
||||
@unittest.skip('to-do list')
|
||||
def test_dir_with_custom_dunders(self):
|
||||
class PlainEnum(Enum):
|
||||
pass
|
||||
cls_dir = dir(PlainEnum)
|
||||
self.assertNotIn('__repr__', cls_dir)
|
||||
self.assertNotIn('__str__', cls_dir)
|
||||
self.assertNotIn('__repr__', cls_dir)
|
||||
self.assertNotIn('__repr__', cls_dir)
|
||||
self.assertNotIn('__format__', cls_dir)
|
||||
self.assertNotIn('__init__', cls_dir)
|
||||
#
|
||||
class MyEnum(Enum):
|
||||
def __repr__(self):
|
||||
|
|
@ -904,8 +905,8 @@ class TestSpecial(unittest.TestCase):
|
|||
cls_dir = dir(MyEnum)
|
||||
self.assertIn('__repr__', cls_dir)
|
||||
self.assertIn('__str__', cls_dir)
|
||||
self.assertIn('__repr__', cls_dir)
|
||||
self.assertIn('__repr__', cls_dir)
|
||||
self.assertIn('__format__', cls_dir)
|
||||
self.assertIn('__init__', cls_dir)
|
||||
|
||||
def test_duplicate_name_error(self):
|
||||
with self.assertRaises(TypeError):
|
||||
|
|
@ -4322,13 +4323,18 @@ class TestConvert(unittest.TestCase):
|
|||
int_dir = dir(int) + [
|
||||
'CONVERT_TEST_NAME_A', 'CONVERT_TEST_NAME_B', 'CONVERT_TEST_NAME_C',
|
||||
'CONVERT_TEST_NAME_D', 'CONVERT_TEST_NAME_E', 'CONVERT_TEST_NAME_F',
|
||||
'CONVERT_TEST_SIGABRT', 'CONVERT_TEST_SIGIOT',
|
||||
'CONVERT_TEST_EIO', 'CONVERT_TEST_EBUS',
|
||||
]
|
||||
extra = [name for name in dir(test_type) if name not in enum_dir(test_type)]
|
||||
missing = [name for name in enum_dir(test_type) if name not in dir(test_type)]
|
||||
self.assertEqual(
|
||||
[name for name in dir(test_type) if name not in int_dir],
|
||||
extra + missing,
|
||||
[],
|
||||
msg='Names other than CONVERT_TEST_* found.',
|
||||
msg='extra names: %r; missing names: %r' % (extra, missing),
|
||||
)
|
||||
|
||||
|
||||
def test_convert_uncomparable(self):
|
||||
uncomp = enum.Enum._convert_(
|
||||
'Uncomparable',
|
||||
|
|
@ -4362,10 +4368,12 @@ class TestConvert(unittest.TestCase):
|
|||
self.assertEqual(test_type.CONVERT_STR_TEST_2, 'goodbye')
|
||||
# Ensure that test_type only picked up names matching the filter.
|
||||
str_dir = dir(str) + ['CONVERT_STR_TEST_1', 'CONVERT_STR_TEST_2']
|
||||
extra = [name for name in dir(test_type) if name not in enum_dir(test_type)]
|
||||
missing = [name for name in enum_dir(test_type) if name not in dir(test_type)]
|
||||
self.assertEqual(
|
||||
[name for name in dir(test_type) if name not in str_dir],
|
||||
extra + missing,
|
||||
[],
|
||||
msg='Names other than CONVERT_STR_* found.',
|
||||
msg='extra names: %r; missing names: %r' % (extra, missing),
|
||||
)
|
||||
self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE)
|
||||
self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye')
|
||||
|
|
@ -4392,25 +4400,22 @@ class TestConvert(unittest.TestCase):
|
|||
# helpers
|
||||
|
||||
def enum_dir(cls):
|
||||
# TODO: check for custom __init__, __new__, __format__, __repr__, __str__, __init_subclass__
|
||||
interesting = set([
|
||||
'__class__', '__contains__', '__doc__', '__getitem__',
|
||||
'__iter__', '__len__', '__members__', '__module__',
|
||||
'__name__', '__qualname__',
|
||||
]
|
||||
+ cls._member_names_
|
||||
)
|
||||
if cls._new_member_ is not object.__new__:
|
||||
interesting.add('__new__')
|
||||
if cls.__init_subclass__ is not object.__init_subclass__:
|
||||
interesting.add('__init_subclass__')
|
||||
if cls._member_type_ is object:
|
||||
interesting = set()
|
||||
if cls.__init_subclass__ is not object.__init_subclass__:
|
||||
interesting.add('__init_subclass__')
|
||||
return sorted(set([
|
||||
'__class__', '__contains__', '__doc__', '__getitem__',
|
||||
'__iter__', '__len__', '__members__', '__module__',
|
||||
'__name__', '__qualname__',
|
||||
]
|
||||
+ cls._member_names_
|
||||
) | interesting
|
||||
)
|
||||
return sorted(interesting)
|
||||
else:
|
||||
# return whatever mixed-in data type has
|
||||
return sorted(set(
|
||||
dir(cls._member_type_)
|
||||
+ cls._member_names_
|
||||
))
|
||||
return sorted(set(dir(cls._member_type_)) | interesting)
|
||||
|
||||
def member_dir(member):
|
||||
if member.__class__._member_type_ is object:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue