← 返回首页
gh-88123: Implement new Enum __contains__ by carlbordum · Pull Request #93298 · python/cpython · GitHub
Skip to content

Navigation Menu

Toggle navigation
Sign in
Appearance settings
Search or jump to...

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Resetting focus
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (2) .rst  (1) All 2 file types selected Viewed files
Conversations
Failed to load comments. Retry
Loading
Jump to
Jump to file
Failed to load files. Retry
Loading
Diff view
Unified
Split
Hide whitespace
Apply and reload
Show whitespace
Diff view
Unified
Split
Hide whitespace
Apply and reload
26 changes: 8 additions & 18 deletions Lib/enum.py
Show comments View file Edit file Delete file Open in desktop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -799,26 +799,16 @@ def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, s
boundary=boundary,
)

def __contains__(cls, member):
"""
Return True if member is a member of this enum
raises TypeError if member is not an enum member
def __contains__(cls, value):
"""Return True if `value` is in `cls`.

note: in 3.12 TypeError will no longer be raised, and True will also be
returned if member is the value of a member in this enum
`value` is in `cls` if:
1) `value` is a member of `cls`, or
2) `value` is the value of one of the `cls`'s members.
"""
if not isinstance(member, Enum):
import warnings
warnings.warn(
"in 3.12 __contains__ will no longer raise TypeError, but will return True or\n"
"False depending on whether the value is a member or the value of a member",
DeprecationWarning,
stacklevel=2,
)
raise TypeError(
"unsupported operand type(s) for 'in': '%s' and '%s'" % (
type(member).__qualname__, cls.__class__.__qualname__))
return isinstance(member, cls) and member._name_ in cls._member_map_
if isinstance(value, cls):
return True
return value in cls._value2member_map_ or value in cls._unhashable_values_

def __delattr__(cls, attr):
# nicer error message when someone tries to delete an attribute
Expand Down
163 changes: 69 additions & 94 deletions Lib/test/test_enum.py
Show comments View file Edit file Delete file Open in desktop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -343,45 +343,56 @@ def test_changing_member_fails(self):
with self.assertRaises(AttributeError):
self.MainEnum.second = 'really first'

@unittest.skipIf(
python_version >= (3, 12),
'__contains__ now returns True/False for all inputs',
)
@unittest.expectedFailure
def test_contains_er(self):
def test_contains_tf(self):
MainEnum = self.MainEnum
self.assertIn(MainEnum.third, MainEnum)
with self.assertRaises(TypeError):
with self.assertWarns(DeprecationWarning):
self.source_values[1] in MainEnum
with self.assertRaises(TypeError):
with self.assertWarns(DeprecationWarning):
'first' in MainEnum
self.assertIn(MainEnum.first, MainEnum)
self.assertTrue(self.values[0] in MainEnum)
if type(self) is not TestStrEnum:
self.assertFalse('first' in MainEnum)
val = MainEnum.dupe
self.assertIn(val, MainEnum)
#
class OtherEnum(Enum):
one = auto()
two = auto()
self.assertNotIn(OtherEnum.two, MainEnum)

@unittest.skipIf(
python_version < (3, 12),
'__contains__ works only with enum memmbers before 3.12',
)
@unittest.expectedFailure
def test_contains_tf(self):
#
if MainEnum._member_type_ is object:
# enums without mixed data types will always be False
class NotEqualEnum(self.enum_type):
this = self.source_values[0]
that = self.source_values[1]
self.assertNotIn(NotEqualEnum.this, MainEnum)
self.assertNotIn(NotEqualEnum.that, MainEnum)
else:
# enums with mixed data types may be True
class EqualEnum(self.enum_type):
this = self.source_values[0]
that = self.source_values[1]
self.assertIn(EqualEnum.this, MainEnum)
self.assertIn(EqualEnum.that, MainEnum)

def test_contains_same_name_diff_enum_diff_values(self):
MainEnum = self.MainEnum
self.assertIn(MainEnum.first, MainEnum)
self.assertTrue(self.source_values[0] in MainEnum)
self.assertFalse('first' in MainEnum)
val = MainEnum.dupe
self.assertIn(val, MainEnum)
#
class OtherEnum(Enum):
one = auto()
two = auto()
self.assertNotIn(OtherEnum.two, MainEnum)
first = "brand"
second = "new"
third = "values"
#
self.assertIn(MainEnum.first, MainEnum)
self.assertIn(MainEnum.second, MainEnum)
self.assertIn(MainEnum.third, MainEnum)
self.assertNotIn(MainEnum.first, OtherEnum)
self.assertNotIn(MainEnum.second, OtherEnum)
self.assertNotIn(MainEnum.third, OtherEnum)
#
self.assertIn(OtherEnum.first, OtherEnum)
self.assertIn(OtherEnum.second, OtherEnum)
self.assertIn(OtherEnum.third, OtherEnum)
self.assertNotIn(OtherEnum.first, MainEnum)
self.assertNotIn(OtherEnum.second, MainEnum)
self.assertNotIn(OtherEnum.third, MainEnum)

def test_dir_on_class(self):
TE = self.MainEnum
Expand Down Expand Up @@ -1115,6 +1126,28 @@ class Huh(Enum):
self.assertEqual(Huh.name.name, 'name')
self.assertEqual(Huh.name.value, 1)

def test_contains_name_and_value_overlap(self):
class IntEnum1(IntEnum):
X = 1
class IntEnum2(IntEnum):
X = 1
class IntEnum3(IntEnum):
X = 2
class IntEnum4(IntEnum):
Y = 1
self.assertIn(IntEnum1.X, IntEnum1)
self.assertIn(IntEnum1.X, IntEnum2)
self.assertNotIn(IntEnum1.X, IntEnum3)
self.assertIn(IntEnum1.X, IntEnum4)

def test_contains_different_types_same_members(self):
class IntEnum1(IntEnum):
X = 1
class IntFlag1(IntFlag):
X = 1
self.assertIn(IntEnum1.X, IntFlag1)
self.assertIn(IntFlag1.X, IntEnum1)

def test_inherited_data_type(self):
class HexInt(int):
__qualname__ = 'HexInt'
Expand Down Expand Up @@ -2969,41 +3002,15 @@ def test_pickle(self):
test_pickle_dump_load(self.assertIs, FlagStooges.CURLY|FlagStooges.MOE)
test_pickle_dump_load(self.assertIs, FlagStooges)

@unittest.skipIf(
python_version >= (3, 12),
'__contains__ now returns True/False for all inputs',
)
@unittest.expectedFailure
def test_contains_er(self):
Open = self.Open
Color = self.Color
self.assertFalse(Color.BLACK in Open)
self.assertFalse(Open.RO in Color)
with self.assertRaises(TypeError):
with self.assertWarns(DeprecationWarning):
'BLACK' in Color
with self.assertRaises(TypeError):
with self.assertWarns(DeprecationWarning):
'RO' in Open
with self.assertRaises(TypeError):
with self.assertWarns(DeprecationWarning):
1 in Color
with self.assertRaises(TypeError):
with self.assertWarns(DeprecationWarning):
1 in Open

@unittest.skipIf(
python_version < (3, 12),
'__contains__ only works with enum memmbers before 3.12',
)
@unittest.expectedFailure
def test_contains_tf(self):
Open = self.Open
Color = self.Color
self.assertFalse(Color.BLACK in Open)
self.assertFalse(Open.RO in Color)
self.assertFalse('BLACK' in Color)
self.assertFalse('RO' in Open)
self.assertTrue(Color.BLACK in Color)
self.assertTrue(Open.RO in Open)
self.assertTrue(1 in Color)
self.assertTrue(1 in Open)

Expand Down Expand Up @@ -3543,43 +3550,11 @@ def test_programatic_function_from_empty_tuple(self):
self.assertEqual(len(lst), len(Thing))
self.assertEqual(len(Thing), 0, Thing)

@unittest.skipIf(
python_version >= (3, 12),
'__contains__ now returns True/False for all inputs',
)
@unittest.expectedFailure
def test_contains_er(self):
Open = self.Open
Color = self.Color
self.assertTrue(Color.GREEN in Color)
self.assertTrue(Open.RW in Open)
self.assertFalse(Color.GREEN in Open)
self.assertFalse(Open.RW in Color)
with self.assertRaises(TypeError):
with self.assertWarns(DeprecationWarning):
'GREEN' in Color
with self.assertRaises(TypeError):
with self.assertWarns(DeprecationWarning):
'RW' in Open
with self.assertRaises(TypeError):
with self.assertWarns(DeprecationWarning):
2 in Color
with self.assertRaises(TypeError):
with self.assertWarns(DeprecationWarning):
2 in Open

@unittest.skipIf(
python_version < (3, 12),
'__contains__ only works with enum memmbers before 3.12',
)
@unittest.expectedFailure
def test_contains_tf(self):
Open = self.Open
Color = self.Color
self.assertTrue(Color.GREEN in Color)
self.assertTrue(Open.RW in Open)
self.assertTrue(Color.GREEN in Open)
self.assertTrue(Open.RW in Color)
self.assertFalse('GREEN' in Color)
self.assertFalse('RW' in Open)
self.assertTrue(2 in Color)
Expand Down Expand Up @@ -4087,12 +4062,12 @@ class Color(enum.Enum)
| ----------------------------------------------------------------------
| Methods inherited from enum.EnumType:
|\x20\x20
| __contains__(member) from enum.EnumType
| Return True if member is a member of this enum
| raises TypeError if member is not an enum member
|\x20\x20\x20\x20\x20\x20
| note: in 3.12 TypeError will no longer be raised, and True will also be
| returned if member is the value of a member in this enum
| __contains__(value) from enum.EnumType
| Return True if `value` is in `cls`.
|
| `value` is in `cls` if:
| 1) `value` is a member of `cls`, or
| 2) `value` is the value of one of the `cls`'s members.
|\x20\x20
| __getitem__(name) from enum.EnumType
| Return the member matching `name`.
Expand Down
Show comments View file Edit file Delete file Open in desktop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Implement Enum __contains__ that returns True or False to replace the
deprecated behaviour that would sometimes raise a TypeError.
Toggle all file notes Toggle all file annotations

Footer

© 2026 GitHub, Inc.