[Python-checkins] bpo-45535: [Enum] include special dunders in dir() (GH-30677)
ethanfurman
webhook-mailer at python.org
Tue Jan 18 18:13:24 EST 2022
https://github.com/python/cpython/commit/7c0914d35eaaab2f323260ba5fe8884732533888
commit: 7c0914d35eaaab2f323260ba5fe8884732533888
branch: main
author: Ethan Furman <ethan at stoneleaf.us>
committer: ethanfurman <ethan at stoneleaf.us>
date: 2022-01-18T15:13:13-08:00
summary:
bpo-45535: [Enum] include special dunders in dir() (GH-30677)
Include the `__dunders__` in `dir()` that make `Enum` special:
- `__contains__`
- `__getitem__`
- `__iter__`
- `__len__`
- `__members__`
files:
M Lib/enum.py
M Lib/test/test_enum.py
diff --git a/Lib/enum.py b/Lib/enum.py
index 772e1eac0e1e6..b510467731293 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -766,29 +766,22 @@ def __delattr__(cls, attr):
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):
"""
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index 18cc2f30ce559..d7ce8add78715 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -883,14 +883,15 @@ class Part(Enum):
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 @@ def __init__(self):
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 @@ def test_convert_int(self):
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 @@ def test_convert_str(self):
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 @@ def test_convert_repr_and_str(self):
# 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:
More information about the Python-checkins
mailing list