summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Stapleton Cordasco <graffatcolmingov@gmail.com>2018-02-17 15:01:03 -0600
committerGitHub <noreply@github.com>2018-02-17 15:01:03 -0600
commiteb91b79ab38aad4d44a3b52b02605412f32ba5ae (patch)
tree1b37b5704da2c84b8dc53c5b9fbc160435c019a2
parent01ecae5a87d2bdf7df6528722e0c2ae36930d90f (diff)
parent1711fb429ae4815faa55bc9ccceca59f27f88706 (diff)
downloadpep8-eb91b79ab38aad4d44a3b52b02605412f32ba5ae.tar.gz
Merge pull request #684 from jdufresne/async-await
Add W606 warning for async and await keywords in Python 3.7
-rw-r--r--CHANGES.txt2
-rw-r--r--docs/intro.rst2
-rwxr-xr-xpycodestyle.py54
-rw-r--r--testsuite/W60.py45
4 files changed, 103 insertions, 0 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index af06645..25b8558 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -9,6 +9,8 @@ New checks:
* Add W504 warning for checking that a break doesn't happen after a binary
operator. This check is ignored by default
* Add W605 warning for invalid escape sequences in string literals
+* Add W606 warning for 'async' and 'await' reserved keywords being introduced
+ in Python 3.7
2.3.1 (2017-01-31)
------------------
diff --git a/docs/intro.rst b/docs/intro.rst
index 854dc54..80ef08f 100644
--- a/docs/intro.rst
+++ b/docs/intro.rst
@@ -419,6 +419,8 @@ This is the current list of error and warning codes:
+------------+----------------------------------------------------------------------+
| W605 | invalid escape sequence '\x' |
+------------+----------------------------------------------------------------------+
+| W606 | 'async' and 'await' are reserved keywords starting with Python 3.7 |
++------------+----------------------------------------------------------------------+
**(*)** In the default configuration, the checks **E121**, **E123**, **E126**,
diff --git a/pycodestyle.py b/pycodestyle.py
index 7131f2a..5f02187 100755
--- a/pycodestyle.py
+++ b/pycodestyle.py
@@ -1507,6 +1507,60 @@ def python_3000_invalid_escape_sequence(logical_line, tokens):
pos = string.find('\\', pos + 1)
+@register_check
+def python_3000_async_await_keywords(logical_line, tokens):
+ """'async' and 'await' are reserved keywords starting with Python 3.7
+
+ W606: async = 42
+ W606: await = 42
+ Okay: async def read_data(db):\n data = await db.fetch('SELECT ...')
+ """
+ # The Python tokenize library before Python 3.5 recognizes async/await as a
+ # NAME token. Therefore, use a state machine to look for the possible
+ # async/await constructs as defined by the Python grammar:
+ # https://docs.python.org/3/reference/grammar.html
+
+ state = None
+ for token_type, text, start, end, line in tokens:
+ error = False
+
+ if state is None:
+ if token_type == tokenize.NAME:
+ if text == 'async':
+ state = ('async_stmt', start)
+ elif text == 'await':
+ state = ('await', start)
+ elif state[0] == 'async_stmt':
+ if token_type == tokenize.NAME and text in ('def', 'with', 'for'):
+ # One of funcdef, with_stmt, or for_stmt. Return to looking
+ # for async/await names.
+ state = None
+ else:
+ error = True
+ elif state[0] == 'await':
+ if token_type in (tokenize.NAME, tokenize.NUMBER, tokenize.STRING):
+ # An await expression. Return to looking for async/await names.
+ state = None
+ else:
+ error = True
+
+ if error:
+ yield (
+ state[1],
+ "W606 'async' and 'await' are reserved keywords starting with "
+ "Python 3.7",
+ )
+ state = None
+
+ # Last token
+ if state is not None:
+ yield (
+ state[1],
+ "W606 'async' and 'await' are reserved keywords starting with "
+ "Python 3.7",
+ )
+
+
##############################################################################
# Helper functions
##############################################################################
diff --git a/testsuite/W60.py b/testsuite/W60.py
index cbe267d..030bec5 100644
--- a/testsuite/W60.py
+++ b/testsuite/W60.py
@@ -29,3 +29,48 @@ regex = r'''
\\.png$
'''
s = '\\'
+#: W606
+async = 42
+#: W606
+await = 42
+#: W606
+def async():
+ pass
+#: W606
+def await():
+ pass
+#: W606
+class async:
+ pass
+#: W606
+class await:
+ pass
+#: Okay
+async def read_data(db):
+ data = await db.fetch('SELECT ...')
+#: Okay
+if await fut:
+ pass
+if (await fut):
+ pass
+if await fut + 1:
+ pass
+if (await fut) + 1:
+ pass
+pair = await fut, 'spam'
+pair = (await fut), 'spam'
+with await fut, open():
+ pass
+with (await fut), open():
+ pass
+await foo()['spam'].baz()()
+return await coro()
+return (await coro())
+res = await coro() ** 2
+res = (await coro()) ** 2
+func(a1=await coro(), a2=0)
+func(a1=(await coro()), a2=0)
+await foo() + await bar()
+(await foo()) + (await bar())
+-await foo()
+-(await foo())