Skip to content

Commit 9430652

Browse files
RJ722ethanfurman
authored andcommitted
bpo-33217: Raise TypeError for non-Enum lookups in Enums (GH-6651)
* bpo-33217: Raise TypeError for non-Enum lookups in Enums
1 parent 51a4743 commit 9430652

File tree

4 files changed

+62
-5
lines changed

4 files changed

+62
-5
lines changed

Doc/library/enum.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,7 @@ Enum Classes
976976
The :class:`EnumMeta` metaclass is responsible for providing the
977977
:meth:`__contains__`, :meth:`__dir__`, :meth:`__iter__` and other methods that
978978
allow one to do things with an :class:`Enum` class that fail on a typical
979-
class, such as `list(Color)` or `some_var in Color`. :class:`EnumMeta` is
979+
class, such as `list(Color)` or `some_enum_var in Color`. :class:`EnumMeta` is
980980
responsible for ensuring that various other methods on the final :class:`Enum`
981981
class are correct (such as :meth:`__new__`, :meth:`__getnewargs__`,
982982
:meth:`__str__` and :meth:`__repr__`).

Lib/enum.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,10 @@ def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, s
303303
return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
304304

305305
def __contains__(cls, member):
306+
if not isinstance(member, Enum):
307+
raise TypeError(
308+
"unsupported operand type(s) for 'in': '%s' and '%s'" % (
309+
type(member).__qualname__, cls.__class__.__qualname__))
306310
return isinstance(member, cls) and member._name_ in cls._member_map_
307311

308312
def __delattr__(cls, attr):
@@ -705,7 +709,9 @@ def _create_pseudo_member_(cls, value):
705709

706710
def __contains__(self, other):
707711
if not isinstance(other, self.__class__):
708-
return NotImplemented
712+
raise TypeError(
713+
"unsupported operand type(s) for 'in': '%s' and '%s'" % (
714+
type(other).__qualname__, self.__class__.__qualname__))
709715
return other._value_ & self._value_ == other._value_
710716

711717
def __repr__(self):

Lib/test/test_enum.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,10 @@ class IntLogic(int, Enum):
325325
def test_contains(self):
326326
Season = self.Season
327327
self.assertIn(Season.AUTUMN, Season)
328-
self.assertNotIn(3, Season)
328+
with self.assertRaises(TypeError):
329+
3 in Season
330+
with self.assertRaises(TypeError):
331+
'AUTUMN' in Season
329332

330333
val = Season(3)
331334
self.assertIn(val, Season)
@@ -1752,6 +1755,13 @@ class Open(Flag):
17521755
AC = 3
17531756
CE = 1<<19
17541757

1758+
class Color(Flag):
1759+
BLACK = 0
1760+
RED = 1
1761+
GREEN = 2
1762+
BLUE = 4
1763+
PURPLE = RED|BLUE
1764+
17551765
def test_str(self):
17561766
Perm = self.Perm
17571767
self.assertEqual(str(Perm.R), 'Perm.R')
@@ -1954,7 +1964,21 @@ def test_pickle(self):
19541964
test_pickle_dump_load(self.assertIs, FlagStooges.CURLY|FlagStooges.MOE)
19551965
test_pickle_dump_load(self.assertIs, FlagStooges)
19561966

1957-
def test_containment(self):
1967+
def test_contains(self):
1968+
Open = self.Open
1969+
Color = self.Color
1970+
self.assertFalse(Color.BLACK in Open)
1971+
self.assertFalse(Open.RO in Color)
1972+
with self.assertRaises(TypeError):
1973+
'BLACK' in Color
1974+
with self.assertRaises(TypeError):
1975+
'RO' in Open
1976+
with self.assertRaises(TypeError):
1977+
1 in Color
1978+
with self.assertRaises(TypeError):
1979+
1 in Open
1980+
1981+
def test_member_contains(self):
19581982
Perm = self.Perm
19591983
R, W, X = Perm
19601984
RW = R | W
@@ -2072,6 +2096,13 @@ class Open(IntFlag):
20722096
AC = 3
20732097
CE = 1<<19
20742098

2099+
class Color(IntFlag):
2100+
BLACK = 0
2101+
RED = 1
2102+
GREEN = 2
2103+
BLUE = 4
2104+
PURPLE = RED|BLUE
2105+
20752106
def test_type(self):
20762107
Perm = self.Perm
20772108
Open = self.Open
@@ -2340,7 +2371,23 @@ def test_programatic_function_from_empty_tuple(self):
23402371
self.assertEqual(len(lst), len(Thing))
23412372
self.assertEqual(len(Thing), 0, Thing)
23422373

2343-
def test_containment(self):
2374+
def test_contains(self):
2375+
Open = self.Open
2376+
Color = self.Color
2377+
self.assertTrue(Color.GREEN in Color)
2378+
self.assertTrue(Open.RW in Open)
2379+
self.assertFalse(Color.GREEN in Open)
2380+
self.assertFalse(Open.RW in Color)
2381+
with self.assertRaises(TypeError):
2382+
'GREEN' in Color
2383+
with self.assertRaises(TypeError):
2384+
'RW' in Open
2385+
with self.assertRaises(TypeError):
2386+
2 in Color
2387+
with self.assertRaises(TypeError):
2388+
2 in Open
2389+
2390+
def test_member_contains(self):
23442391
Perm = self.Perm
23452392
R, W, X = Perm
23462393
RW = R | W
@@ -2359,6 +2406,8 @@ def test_containment(self):
23592406
self.assertFalse(R in WX)
23602407
self.assertFalse(W in RX)
23612408
self.assertFalse(X in RW)
2409+
with self.assertRaises(TypeError):
2410+
self.assertFalse('test' in RW)
23622411

23632412
def test_bool(self):
23642413
Perm = self.Perm
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Raise :exc:`TypeError` when looking up non-Enum objects in Enum classes and
2+
Enum members.

0 commit comments

Comments
 (0)