]> granicus.if.org Git - python/commitdiff
Issue #26182: Raise DeprecationWarning for improper use of async/await keywords
authorYury Selivanov <yury@magic.io>
Thu, 15 Sep 2016 16:50:23 +0000 (12:50 -0400)
committerYury Selivanov <yury@magic.io>
Thu, 15 Sep 2016 16:50:23 +0000 (12:50 -0400)
Lib/asyncio/tasks.py
Lib/test/test_coroutines.py
Lib/test/test_grammar.py
Lib/xml/dom/xmlbuilder.py
Misc/NEWS
Python/ast.c

index 35c945c4368e457e3159919839dff5f0aaaa5623..4c66546428b9dd92ffa3fd810ef7bb080bd2dfea 100644 (file)
@@ -519,7 +519,7 @@ def sleep(delay, result=None, *, loop=None):
         h.cancel()
 
 
-def async(coro_or_future, *, loop=None):
+def async_(coro_or_future, *, loop=None):
     """Wrap a coroutine in a future.
 
     If the argument is a Future, it is returned directly.
@@ -532,6 +532,11 @@ def async(coro_or_future, *, loop=None):
 
     return ensure_future(coro_or_future, loop=loop)
 
+# Silence DeprecationWarning:
+globals()['async'] = async_
+async_.__name__ = 'async'
+del async_
+
 
 def ensure_future(coro_or_future, *, loop=None):
     """Wrap a coroutine or an awaitable in a future.
index 154ce7fdee18d6f83eb6bdfeef5c5d4f5ccfb650..f2839a719a3ab766faec01c17672ee0bf51f932f 100644 (file)
@@ -358,57 +358,110 @@ class AsyncBadSyntaxTest(unittest.TestCase):
             with self.subTest(code=code), self.assertRaises(SyntaxError):
                 compile(code, "<test>", "exec")
 
-    def test_goodsyntax_1(self):
-        # Tests for issue 24619
+    def test_badsyntax_2(self):
+        samples = [
+            """def foo():
+                await = 1
+            """,
+
+            """class Bar:
+                def async(): pass
+            """,
 
-        def foo(await):
-            async def foo(): pass
-            async def foo():
+            """class Bar:
+                async = 1
+            """,
+
+            """class async:
                 pass
-            return await + 1
-        self.assertEqual(foo(10), 11)
+            """,
 
-        def foo(await):
-            async def foo(): pass
-            async def foo(): pass
-            return await + 2
-        self.assertEqual(foo(20), 22)
+            """class await:
+                pass
+            """,
 
-        def foo(await):
+            """import math as await""",
 
-            async def foo(): pass
+            """def async():
+                pass""",
 
-            async def foo(): pass
+            """def foo(*, await=1):
+                pass"""
 
-            return await + 2
-        self.assertEqual(foo(20), 22)
+            """async = 1""",
 
-        def foo(await):
-            """spam"""
-            async def foo(): \
-                pass
-            # 123
-            async def foo(): pass
-            # 456
-            return await + 2
-        self.assertEqual(foo(20), 22)
-
-        def foo(await):
-            def foo(): pass
-            def foo(): pass
-            async def bar(): return await_
-            await_ = await
-            try:
-                bar().send(None)
-            except StopIteration as ex:
-                return ex.args[0]
-        self.assertEqual(foo(42), 42)
+            """print(await=1)"""
+        ]
 
-        async def f():
-            async def g(): pass
-            await z
-        await = 1
-        self.assertTrue(inspect.iscoroutinefunction(f))
+        for code in samples:
+            with self.subTest(code=code), self.assertWarnsRegex(
+                    DeprecationWarning,
+                    "'await' will become reserved keywords"):
+                compile(code, "<test>", "exec")
+
+    def test_badsyntax_3(self):
+        with self.assertRaises(DeprecationWarning):
+            with warnings.catch_warnings():
+                warnings.simplefilter("error")
+                compile("async = 1", "<test>", "exec")
+
+    def test_goodsyntax_1(self):
+        # Tests for issue 24619
+
+        samples = [
+            '''def foo(await):
+                async def foo(): pass
+                async def foo():
+                    pass
+                return await + 1
+            ''',
+
+            '''def foo(await):
+                async def foo(): pass
+                async def foo(): pass
+                return await + 1
+            ''',
+
+            '''def foo(await):
+
+                async def foo(): pass
+
+                async def foo(): pass
+
+                return await + 1
+            ''',
+
+            '''def foo(await):
+                """spam"""
+                async def foo(): \
+                    pass
+                # 123
+                async def foo(): pass
+                # 456
+                return await + 1
+            ''',
+
+            '''def foo(await):
+                def foo(): pass
+                def foo(): pass
+                async def bar(): return await_
+                await_ = await
+                try:
+                    bar().send(None)
+                except StopIteration as ex:
+                    return ex.args[0] + 1
+            '''
+        ]
+
+        for code in samples:
+            with self.subTest(code=code):
+                loc = {}
+
+                with warnings.catch_warnings():
+                    warnings.simplefilter("ignore")
+                    exec(code, loc, loc)
+
+                self.assertEqual(loc['foo'](10), 11)
 
 
 class TokenizerRegrTest(unittest.TestCase):
index 67a61d4ab5b65edb0277744f4a5b6547a94fa20d..03f2c84211e6a4d9027f2b2d0a4e143bafb76936 100644 (file)
@@ -1345,18 +1345,6 @@ class GrammarTests(unittest.TestCase):
         self.assertEqual(m.other, 42)
 
     def test_async_await(self):
-        async = 1
-        await = 2
-        self.assertEqual(async, 1)
-
-        def async():
-            nonlocal await
-            await = 10
-        async()
-        self.assertEqual(await, 10)
-
-        self.assertFalse(bool(async.__code__.co_flags & inspect.CO_COROUTINE))
-
         async def test():
             def sum():
                 pass
index 444f0b2a57d3df183c3c19153be6173fa0436721..e9a1536472cd55ec117429106da244c823696d66 100644 (file)
@@ -353,14 +353,14 @@ class _AsyncDeprecatedProperty:
 class DocumentLS:
     """Mixin to create documents that conform to the load/save spec."""
 
-    async = _AsyncDeprecatedProperty()
     async_ = False
+    locals()['async'] = _AsyncDeprecatedProperty()  # Avoid DeprecationWarning
 
     def _get_async(self):
         return False
 
-    def _set_async(self, async):
-        if async:
+    def _set_async(self, flag):
+        if flag:
             raise xml.dom.NotSupportedErr(
                 "asynchronous document loading is not supported")
 
index 4c23930cddbf767bbbc1338c4b00fc56ef66af51..4655b5ea7dab0e710bf1490d672b393db34438a4 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -21,6 +21,9 @@ Core and Builtins
 - Issue #28120: Fix dict.pop() for splitted dictionary when trying to remove a
   "pending key" (Not yet inserted in split-table). Patch by Xiang Zhang.
 
+- Issue #26182: Raise DeprecationWarning when async and await keywords are
+  used as variable/attribute/class/function name.
+
 Library
 -------
 
index 765d24e11b89abb5d3197863f4087cfda1606023..deea579c0ddfd05372818228031793b7afe3b8f0 100644 (file)
@@ -938,6 +938,26 @@ forbidden_name(struct compiling *c, identifier name, const node *n,
         ast_error(c, n, "assignment to keyword");
         return 1;
     }
+    if (PyUnicode_CompareWithASCIIString(name, "async") == 0 ||
+        PyUnicode_CompareWithASCIIString(name, "await") == 0)
+    {
+        PyObject *message = PyUnicode_FromString(
+            "'async' and 'await' will become reserved keywords"
+            " in Python 3.7");
+        if (message == NULL) {
+            return 1;
+        }
+        if (PyErr_WarnExplicitObject(
+                PyExc_DeprecationWarning,
+                message,
+                c->c_filename,
+                LINENO(n),
+                NULL,
+                NULL) < 0)
+        {
+            return 1;
+        }
+    }
     if (full_checks) {
         const char * const *p;
         for (p = FORBIDDEN; *p; p++) {