Skip to content

Commit 3715176

Browse files
authored
[3.7] bpo-33217: deprecate non-Enum lookups in Enums (GH-6392)
deprecate non-Enum lookups in Enums Lookups such as `1 in Color` and `2 in SomeFlag()` will raise TypeError in 3.8+.
1 parent cbbf746 commit 3715176

5 files changed

Lines changed: 78 additions & 5 deletions

File tree

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__`).

Doc/whatsnew/3.7.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,12 @@ Deprecated
10411041
:meth:`ssl.SSLContext.wrap_socket` instead.
10421042
(Contributed by Christian Heimes in :issue:`28124`.)
10431043

1044+
- In Python 3.8, attempting to check for non-Enum objects in :class:`Enum`
1045+
classes will raise a :exc:`TypeError` (e.g. ``1 in Color``); similarly,
1046+
attempting to check for non-Flag objects in a :class:`Flag` member will
1047+
raise :exc:`TypeError` (e.g. ``1 in Perm.RW``); currently, both operations
1048+
return :const:`False` instead.
1049+
10441050

10451051
Windows Only
10461052
------------

Lib/enum.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,12 @@ def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, s
309309
return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
310310

311311
def __contains__(cls, member):
312+
if not isinstance(member, Enum):
313+
import warnings
314+
warnings.warn(
315+
"using non-Enums in containment checks will raise "
316+
"TypeError in Python 3.8",
317+
DeprecationWarning, 2)
312318
return isinstance(member, cls) and member._name_ in cls._member_map_
313319

314320
def __delattr__(cls, attr):
@@ -713,7 +719,12 @@ def _create_pseudo_member_(cls, value):
713719

714720
def __contains__(self, other):
715721
if not isinstance(other, self.__class__):
716-
return NotImplemented
722+
import warnings
723+
warnings.warn(
724+
"using non-Flags in containment checks will raise "
725+
"TypeError in Python 3.8",
726+
DeprecationWarning, 2)
727+
return False
717728
return other._value_ & self._value_ == other._value_
718729

719730
def __repr__(self):

Lib/test/test_enum.py

Lines changed: 57 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.assertWarns(DeprecationWarning):
329+
self.assertNotIn(3, Season)
330+
with self.assertWarns(DeprecationWarning):
331+
self.assertNotIn('AUTUMN', Season)
329332

330333
val = Season(3)
331334
self.assertIn(val, Season)
@@ -334,6 +337,11 @@ class OtherEnum(Enum):
334337
one = 1; two = 2
335338
self.assertNotIn(OtherEnum.two, Season)
336339

340+
def test_member_contains(self):
341+
self.assertRaises(TypeError, lambda: 'test' in self.Season.AUTUMN)
342+
self.assertRaises(TypeError, lambda: 3 in self.Season.AUTUMN)
343+
self.assertRaises(TypeError, lambda: 'AUTUMN' in self.Season.AUTUMN)
344+
337345
def test_comparisons(self):
338346
Season = self.Season
339347
with self.assertRaises(TypeError):
@@ -1745,6 +1753,13 @@ class TestFlag(unittest.TestCase):
17451753
class Perm(Flag):
17461754
R, W, X = 4, 2, 1
17471755

1756+
class Color(Flag):
1757+
BLACK = 0
1758+
RED = 1
1759+
GREEN = 2
1760+
BLUE = 4
1761+
PURPLE = RED|BLUE
1762+
17481763
class Open(Flag):
17491764
RO = 0
17501765
WO = 1
@@ -1954,7 +1969,21 @@ def test_pickle(self):
19541969
test_pickle_dump_load(self.assertIs, FlagStooges.CURLY|FlagStooges.MOE)
19551970
test_pickle_dump_load(self.assertIs, FlagStooges)
19561971

1957-
def test_containment(self):
1972+
def test_contains(self):
1973+
Open = self.Open
1974+
Color = self.Color
1975+
self.assertFalse(Color.BLACK in Open)
1976+
self.assertFalse(Open.RO in Color)
1977+
with self.assertWarns(DeprecationWarning):
1978+
self.assertFalse('BLACK' in Color)
1979+
with self.assertWarns(DeprecationWarning):
1980+
self.assertFalse('RO' in Open)
1981+
with self.assertWarns(DeprecationWarning):
1982+
self.assertFalse(1 in Color)
1983+
with self.assertWarns(DeprecationWarning):
1984+
self.assertFalse(1 in Open)
1985+
1986+
def test_member_contains(self):
19581987
Perm = self.Perm
19591988
R, W, X = Perm
19601989
RW = R | W
@@ -2065,6 +2094,13 @@ class Perm(IntFlag):
20652094
W = 1 << 1
20662095
R = 1 << 2
20672096

2097+
class Color(IntFlag):
2098+
BLACK = 0
2099+
RED = 1
2100+
GREEN = 2
2101+
BLUE = 4
2102+
PURPLE = RED|BLUE
2103+
20682104
class Open(IntFlag):
20692105
RO = 0
20702106
WO = 1
@@ -2340,7 +2376,23 @@ def test_programatic_function_from_empty_tuple(self):
23402376
self.assertEqual(len(lst), len(Thing))
23412377
self.assertEqual(len(Thing), 0, Thing)
23422378

2343-
def test_containment(self):
2379+
def test_contains(self):
2380+
Color = self.Color
2381+
Open = self.Open
2382+
self.assertTrue(Color.GREEN in Color)
2383+
self.assertTrue(Open.RW in Open)
2384+
self.assertFalse(Color.GREEN in Open)
2385+
self.assertFalse(Open.RW in Color)
2386+
with self.assertWarns(DeprecationWarning):
2387+
self.assertFalse('GREEN' in Color)
2388+
with self.assertWarns(DeprecationWarning):
2389+
self.assertFalse('RW' in Open)
2390+
with self.assertWarns(DeprecationWarning):
2391+
self.assertFalse(2 in Color)
2392+
with self.assertWarns(DeprecationWarning):
2393+
self.assertFalse(2 in Open)
2394+
2395+
def test_member_contains(self):
23442396
Perm = self.Perm
23452397
R, W, X = Perm
23462398
RW = R | W
@@ -2359,6 +2411,8 @@ def test_containment(self):
23592411
self.assertFalse(R in WX)
23602412
self.assertFalse(W in RX)
23612413
self.assertFalse(X in RW)
2414+
with self.assertWarns(DeprecationWarning):
2415+
self.assertFalse('swallow' in RW)
23622416

23632417
def test_bool(self):
23642418
Perm = self.Perm
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Deprecate looking up non-Enum objects in Enum classes and Enum members (will
2+
raise :exc:`TypeError` in 3.8+).

0 commit comments

Comments
 (0)