]> granicus.if.org Git - python/commitdiff
[3.7] bpo-33217: deprecate non-Enum lookups in Enums (GH-6392)
authorEthan Furman <ethan@stoneleaf.us>
Thu, 12 Apr 2018 01:56:25 +0000 (18:56 -0700)
committerGitHub <noreply@github.com>
Thu, 12 Apr 2018 01:56:25 +0000 (18:56 -0700)
deprecate non-Enum lookups in Enums

Lookups such as `1 in Color` and `2 in SomeFlag()` will raise TypeError
in 3.8+.

Doc/library/enum.rst
Doc/whatsnew/3.7.rst
Lib/enum.py
Lib/test/test_enum.py
Misc/NEWS.d/next/Library/2018-04-05-13-36-09.bpo-33217.FfOKDI.rst [new file with mode: 0644]

index fc65a3d078f19c27d3c4b05e139115c6b7782401..787670c6ddad4ed5ecfbaa1cf7cd4b5be15c175a 100644 (file)
@@ -976,7 +976,7 @@ Enum Classes
 The :class:`EnumMeta` metaclass is responsible for providing the
 :meth:`__contains__`, :meth:`__dir__`, :meth:`__iter__` and other methods that
 allow one to do things with an :class:`Enum` class that fail on a typical
-class, such as `list(Color)` or `some_var in Color`.  :class:`EnumMeta` is
+class, such as `list(Color)` or `some_enum_var in Color`.  :class:`EnumMeta` is
 responsible for ensuring that various other methods on the final :class:`Enum`
 class are correct (such as :meth:`__new__`, :meth:`__getnewargs__`,
 :meth:`__str__` and :meth:`__repr__`).
index 08c7b12574c6cbe5e8bdf32dc8f7519d36446ea4..3a170c07ab4f322683447b9b99117c193dcd9906 100644 (file)
@@ -1041,6 +1041,12 @@ Deprecated
   :meth:`ssl.SSLContext.wrap_socket` instead.
   (Contributed by Christian Heimes in :issue:`28124`.)
 
+- In Python 3.8, attempting to check for non-Enum objects in :class:`Enum`
+  classes will raise a :exc:`TypeError` (e.g. ``1 in Color``); similarly,
+  attempting to check for non-Flag objects in a :class:`Flag` member will
+  raise :exc:`TypeError` (e.g. ``1 in Perm.RW``); currently, both operations
+  return :const:`False` instead.
+
 
 Windows Only
 ------------
index e5fe6f3b94a1d0f9c2cd02b2797cbbc5f79f5f9e..8c8409bd4efd2907a1be85f70c4253ca8a0b3535 100644 (file)
@@ -309,6 +309,12 @@ class EnumMeta(type):
         return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
 
     def __contains__(cls, member):
+        if not isinstance(member, Enum):
+            import warnings
+            warnings.warn(
+                    "using non-Enums in containment checks will raise "
+                    "TypeError in Python 3.8",
+                    DeprecationWarning, 2)
         return isinstance(member, cls) and member._name_ in cls._member_map_
 
     def __delattr__(cls, attr):
@@ -713,7 +719,12 @@ class Flag(Enum):
 
     def __contains__(self, other):
         if not isinstance(other, self.__class__):
-            return NotImplemented
+            import warnings
+            warnings.warn(
+                    "using non-Flags in containment checks will raise "
+                    "TypeError in Python 3.8",
+                    DeprecationWarning, 2)
+            return False
         return other._value_ & self._value_ == other._value_
 
     def __repr__(self):
index 97559712b1dc2ccaa519bff9df7c49a2967b961d..ef2d1daaf9420dc670cd07ffb4a2fae7e5cad5c3 100644 (file)
@@ -325,7 +325,10 @@ class TestEnum(unittest.TestCase):
     def test_contains(self):
         Season = self.Season
         self.assertIn(Season.AUTUMN, Season)
-        self.assertNotIn(3, Season)
+        with self.assertWarns(DeprecationWarning):
+            self.assertNotIn(3, Season)
+        with self.assertWarns(DeprecationWarning):
+            self.assertNotIn('AUTUMN', Season)
 
         val = Season(3)
         self.assertIn(val, Season)
@@ -334,6 +337,11 @@ class TestEnum(unittest.TestCase):
             one = 1; two = 2
         self.assertNotIn(OtherEnum.two, Season)
 
+    def test_member_contains(self):
+        self.assertRaises(TypeError, lambda: 'test' in self.Season.AUTUMN)
+        self.assertRaises(TypeError, lambda: 3 in self.Season.AUTUMN)
+        self.assertRaises(TypeError, lambda: 'AUTUMN' in self.Season.AUTUMN)
+
     def test_comparisons(self):
         Season = self.Season
         with self.assertRaises(TypeError):
@@ -1745,6 +1753,13 @@ class TestFlag(unittest.TestCase):
     class Perm(Flag):
         R, W, X = 4, 2, 1
 
+    class Color(Flag):
+        BLACK = 0
+        RED = 1
+        GREEN = 2
+        BLUE = 4
+        PURPLE = RED|BLUE
+
     class Open(Flag):
         RO = 0
         WO = 1
@@ -1954,7 +1969,21 @@ class TestFlag(unittest.TestCase):
         test_pickle_dump_load(self.assertIs, FlagStooges.CURLY|FlagStooges.MOE)
         test_pickle_dump_load(self.assertIs, FlagStooges)
 
-    def test_containment(self):
+    def test_contains(self):
+        Open = self.Open
+        Color = self.Color
+        self.assertFalse(Color.BLACK in Open)
+        self.assertFalse(Open.RO in Color)
+        with self.assertWarns(DeprecationWarning):
+            self.assertFalse('BLACK' in Color)
+        with self.assertWarns(DeprecationWarning):
+            self.assertFalse('RO' in Open)
+        with self.assertWarns(DeprecationWarning):
+            self.assertFalse(1 in Color)
+        with self.assertWarns(DeprecationWarning):
+            self.assertFalse(1 in Open)
+
+    def test_member_contains(self):
         Perm = self.Perm
         R, W, X = Perm
         RW = R | W
@@ -2065,6 +2094,13 @@ class TestIntFlag(unittest.TestCase):
         W = 1 << 1
         R = 1 << 2
 
+    class Color(IntFlag):
+        BLACK = 0
+        RED = 1
+        GREEN = 2
+        BLUE = 4
+        PURPLE = RED|BLUE
+
     class Open(IntFlag):
         RO = 0
         WO = 1
@@ -2340,7 +2376,23 @@ class TestIntFlag(unittest.TestCase):
         self.assertEqual(len(lst), len(Thing))
         self.assertEqual(len(Thing), 0, Thing)
 
-    def test_containment(self):
+    def test_contains(self):
+        Color = self.Color
+        Open = self.Open
+        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.assertWarns(DeprecationWarning):
+            self.assertFalse('GREEN' in Color)
+        with self.assertWarns(DeprecationWarning):
+            self.assertFalse('RW' in Open)
+        with self.assertWarns(DeprecationWarning):
+            self.assertFalse(2 in Color)
+        with self.assertWarns(DeprecationWarning):
+            self.assertFalse(2 in Open)
+
+    def test_member_contains(self):
         Perm = self.Perm
         R, W, X = Perm
         RW = R | W
@@ -2359,6 +2411,8 @@ class TestIntFlag(unittest.TestCase):
         self.assertFalse(R in WX)
         self.assertFalse(W in RX)
         self.assertFalse(X in RW)
+        with self.assertWarns(DeprecationWarning):
+            self.assertFalse('swallow' in RW)
 
     def test_bool(self):
         Perm = self.Perm
diff --git a/Misc/NEWS.d/next/Library/2018-04-05-13-36-09.bpo-33217.FfOKDI.rst b/Misc/NEWS.d/next/Library/2018-04-05-13-36-09.bpo-33217.FfOKDI.rst
new file mode 100644 (file)
index 0000000..8dfc9e5
--- /dev/null
@@ -0,0 +1,2 @@
+Deprecate looking up non-Enum objects in Enum classes and Enum members (will
+raise :exc:`TypeError` in 3.8+).