Skip to content

Commit eb91b79

Browse files
authored
Merge pull request #684 from jdufresne/async-await
Add W606 warning for async and await keywords in Python 3.7
2 parents 01ecae5 + 1711fb4 commit eb91b79

File tree

4 files changed

+103
-0
lines changed

4 files changed

+103
-0
lines changed

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ New checks:
99
* Add W504 warning for checking that a break doesn't happen after a binary
1010
operator. This check is ignored by default
1111
* Add W605 warning for invalid escape sequences in string literals
12+
* Add W606 warning for 'async' and 'await' reserved keywords being introduced
13+
in Python 3.7
1214

1315
2.3.1 (2017-01-31)
1416
------------------

docs/intro.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ This is the current list of error and warning codes:
419419
+------------+----------------------------------------------------------------------+
420420
| W605 | invalid escape sequence '\x' |
421421
+------------+----------------------------------------------------------------------+
422+
| W606 | 'async' and 'await' are reserved keywords starting with Python 3.7 |
423+
+------------+----------------------------------------------------------------------+
422424

423425

424426
**(*)** In the default configuration, the checks **E121**, **E123**, **E126**,

pycodestyle.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,6 +1507,60 @@ def python_3000_invalid_escape_sequence(logical_line, tokens):
15071507
pos = string.find('\\', pos + 1)
15081508

15091509

1510+
@register_check
1511+
def python_3000_async_await_keywords(logical_line, tokens):
1512+
"""'async' and 'await' are reserved keywords starting with Python 3.7
1513+
1514+
W606: async = 42
1515+
W606: await = 42
1516+
Okay: async def read_data(db):\n data = await db.fetch('SELECT ...')
1517+
"""
1518+
# The Python tokenize library before Python 3.5 recognizes async/await as a
1519+
# NAME token. Therefore, use a state machine to look for the possible
1520+
# async/await constructs as defined by the Python grammar:
1521+
# https://docs.python.org/3/reference/grammar.html
1522+
1523+
state = None
1524+
for token_type, text, start, end, line in tokens:
1525+
error = False
1526+
1527+
if state is None:
1528+
if token_type == tokenize.NAME:
1529+
if text == 'async':
1530+
state = ('async_stmt', start)
1531+
elif text == 'await':
1532+
state = ('await', start)
1533+
elif state[0] == 'async_stmt':
1534+
if token_type == tokenize.NAME and text in ('def', 'with', 'for'):
1535+
# One of funcdef, with_stmt, or for_stmt. Return to looking
1536+
# for async/await names.
1537+
state = None
1538+
else:
1539+
error = True
1540+
elif state[0] == 'await':
1541+
if token_type in (tokenize.NAME, tokenize.NUMBER, tokenize.STRING):
1542+
# An await expression. Return to looking for async/await names.
1543+
state = None
1544+
else:
1545+
error = True
1546+
1547+
if error:
1548+
yield (
1549+
state[1],
1550+
"W606 'async' and 'await' are reserved keywords starting with "
1551+
"Python 3.7",
1552+
)
1553+
state = None
1554+
1555+
# Last token
1556+
if state is not None:
1557+
yield (
1558+
state[1],
1559+
"W606 'async' and 'await' are reserved keywords starting with "
1560+
"Python 3.7",
1561+
)
1562+
1563+
15101564
##############################################################################
15111565
# Helper functions
15121566
##############################################################################

testsuite/W60.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,48 @@
2929
\\.png$
3030
'''
3131
s = '\\'
32+
#: W606
33+
async = 42
34+
#: W606
35+
await = 42
36+
#: W606
37+
def async():
38+
pass
39+
#: W606
40+
def await():
41+
pass
42+
#: W606
43+
class async:
44+
pass
45+
#: W606
46+
class await:
47+
pass
48+
#: Okay
49+
async def read_data(db):
50+
data = await db.fetch('SELECT ...')
51+
#: Okay
52+
if await fut:
53+
pass
54+
if (await fut):
55+
pass
56+
if await fut + 1:
57+
pass
58+
if (await fut) + 1:
59+
pass
60+
pair = await fut, 'spam'
61+
pair = (await fut), 'spam'
62+
with await fut, open():
63+
pass
64+
with (await fut), open():
65+
pass
66+
await foo()['spam'].baz()()
67+
return await coro()
68+
return (await coro())
69+
res = await coro() ** 2
70+
res = (await coro()) ** 2
71+
func(a1=await coro(), a2=0)
72+
func(a1=(await coro()), a2=0)
73+
await foo() + await bar()
74+
(await foo()) + (await bar())
75+
-await foo()
76+
-(await foo())

0 commit comments

Comments
 (0)