Skip to content

Commit 1711fb4

Browse files
committed
Add W606 warning for async and await keywords in Python 3.7
From https://docs.python.org/3/whatsnew/3.6.html#new-keywords > async and await are not recommended to be used as variable, class, > function or module names. Introduced by PEP 492 in Python 3.5, they > will become proper keywords in Python 3.7. Starting in Python 3.6, the > use of async or await as names will generate a DeprecationWarning. By adding a warning to pycodestyle.py these future warnings and syntax errors can be caught during static code analysis. The await expression tests were taken from PEP-492. https://www.python.org/dev/peps/pep-0492/#id58
1 parent c7448b4 commit 1711fb4

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
@@ -7,6 +7,8 @@ UNRELEASED
77
New checks:
88

99
* Add W605 warning for invalid escape sequences in string literals
10+
* Add W606 warning for 'async' and 'await' reserved keywords being introduced
11+
in Python 3.7
1012

1113
2.3.1 (2017-01-31)
1214
------------------

docs/intro.rst

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

419421

420422
**(*)** 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
@@ -1439,6 +1439,60 @@ def python_3000_invalid_escape_sequence(logical_line, tokens):
14391439
pos = string.find('\\', pos + 1)
14401440

14411441

1442+
@register_check
1443+
def python_3000_async_await_keywords(logical_line, tokens):
1444+
"""'async' and 'await' are reserved keywords starting with Python 3.7
1445+
1446+
W606: async = 42
1447+
W606: await = 42
1448+
Okay: async def read_data(db):\n data = await db.fetch('SELECT ...')
1449+
"""
1450+
# The Python tokenize library before Python 3.5 recognizes async/await as a
1451+
# NAME token. Therefore, use a state machine to look for the possible
1452+
# async/await constructs as defined by the Python grammar:
1453+
# https://docs.python.org/3/reference/grammar.html
1454+
1455+
state = None
1456+
for token_type, text, start, end, line in tokens:
1457+
error = False
1458+
1459+
if state is None:
1460+
if token_type == tokenize.NAME:
1461+
if text == 'async':
1462+
state = ('async_stmt', start)
1463+
elif text == 'await':
1464+
state = ('await', start)
1465+
elif state[0] == 'async_stmt':
1466+
if token_type == tokenize.NAME and text in ('def', 'with', 'for'):
1467+
# One of funcdef, with_stmt, or for_stmt. Return to looking
1468+
# for async/await names.
1469+
state = None
1470+
else:
1471+
error = True
1472+
elif state[0] == 'await':
1473+
if token_type in (tokenize.NAME, tokenize.NUMBER, tokenize.STRING):
1474+
# An await expression. Return to looking for async/await names.
1475+
state = None
1476+
else:
1477+
error = True
1478+
1479+
if error:
1480+
yield (
1481+
state[1],
1482+
"W606 'async' and 'await' are reserved keywords starting with "
1483+
"Python 3.7",
1484+
)
1485+
state = None
1486+
1487+
# Last token
1488+
if state is not None:
1489+
yield (
1490+
state[1],
1491+
"W606 'async' and 'await' are reserved keywords starting with "
1492+
"Python 3.7",
1493+
)
1494+
1495+
14421496
##############################################################################
14431497
# Helper functions
14441498
##############################################################################

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)