diff options
Diffstat (limited to 'pycodestyle.py')
-rwxr-xr-x | pycodestyle.py | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/pycodestyle.py b/pycodestyle.py index d31ac9e..ad67fb0 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -1439,6 +1439,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 ############################################################################## |