Commit d930a704 authored by Stefan Behnel's avatar Stefan Behnel

disable async/await keywords in nested normal 'def' functions (as CPython does)

parent 471d145c
...@@ -2039,18 +2039,14 @@ def p_statement(s, ctx, first_statement = 0): ...@@ -2039,18 +2039,14 @@ def p_statement(s, ctx, first_statement = 0):
return p_async_statement(s, ctx, decorators) return p_async_statement(s, ctx, decorators)
else: else:
if s.sy == 'IDENT' and s.systring == 'async': if s.sy == 'IDENT' and s.systring == 'async':
ident_name = s.systring
# PEP 492 enables the async/await keywords when it spots "async def ..." # PEP 492 enables the async/await keywords when it spots "async def ..."
s.next() s.next()
if s.sy == 'def': if s.sy == 'def':
s.enable_keyword('async') return p_async_statement(s, ctx, decorators)
s.enable_keyword('await')
result = p_async_statement(s, ctx, decorators)
s.disable_keyword('await')
s.disable_keyword('async')
return result
elif decorators: elif decorators:
s.error("Decorators can only be followed by functions or classes") s.error("Decorators can only be followed by functions or classes")
s.put_back('IDENT', 'async') s.put_back('IDENT', ident_name) # re-insert original token
return p_simple_statement_list(s, ctx, first_statement=first_statement) return p_simple_statement_list(s, ctx, first_statement=first_statement)
...@@ -3072,7 +3068,25 @@ def p_def_statement(s, decorators=None, is_async_def=False): ...@@ -3072,7 +3068,25 @@ def p_def_statement(s, decorators=None, is_async_def=False):
if s.sy == '->': if s.sy == '->':
s.next() s.next()
return_type_annotation = p_test(s) return_type_annotation = p_test(s)
# PEP 492 switches the async/await keywords off in simple "def" functions
# and on in "async def" functions
await_was_enabled = s.enable_keyword('await') if is_async_def else s.disable_keyword('await')
async_was_enabled = s.enable_keyword('async') if is_async_def else s.disable_keyword('async')
doc, body = p_suite_with_docstring(s, Ctx(level='function')) doc, body = p_suite_with_docstring(s, Ctx(level='function'))
if is_async_def:
if not async_was_enabled:
s.disable_keyword('async')
if not await_was_enabled:
s.disable_keyword('await')
else:
if async_was_enabled:
s.enable_keyword('async')
if await_was_enabled:
s.enable_keyword('await')
return Nodes.DefNode( return Nodes.DefNode(
pos, name=name, args=args, star_arg=star_arg, starstar_arg=starstar_arg, pos, name=name, args=args, star_arg=star_arg, starstar_arg=starstar_arg,
doc=doc, body=body, decorators=decorators, is_async_def=is_async_def, doc=doc, body=body, decorators=decorators, is_async_def=is_async_def,
......
...@@ -30,7 +30,6 @@ cdef class PyrexScanner(Scanner): ...@@ -30,7 +30,6 @@ cdef class PyrexScanner(Scanner):
cdef public bint in_python_file cdef public bint in_python_file
cdef public source_encoding cdef public source_encoding
cdef set keywords cdef set keywords
cdef public dict keywords_stack
cdef public list indentation_stack cdef public list indentation_stack
cdef public indentation_char cdef public indentation_char
cdef public int bracket_nesting_level cdef public int bracket_nesting_level
...@@ -58,5 +57,5 @@ cdef class PyrexScanner(Scanner): ...@@ -58,5 +57,5 @@ cdef class PyrexScanner(Scanner):
cdef expect_indent(self) cdef expect_indent(self)
cdef expect_dedent(self) cdef expect_dedent(self)
cdef expect_newline(self, message=*, bint ignore_semicolon=*) cdef expect_newline(self, message=*, bint ignore_semicolon=*)
cdef enable_keyword(self, name) cdef bint enable_keyword(self, name) except -1
cdef disable_keyword(self, name) cdef bint disable_keyword(self, name) except -1
...@@ -314,7 +314,6 @@ class PyrexScanner(Scanner): ...@@ -314,7 +314,6 @@ class PyrexScanner(Scanner):
self.in_python_file = False self.in_python_file = False
self.keywords = set(pyx_reserved_words) self.keywords = set(pyx_reserved_words)
self.trace = trace_scanner self.trace = trace_scanner
self.keywords_stack = {}
self.indentation_stack = [0] self.indentation_stack = [0]
self.indentation_char = None self.indentation_char = None
self.bracket_nesting_level = 0 self.bracket_nesting_level = 0
...@@ -495,16 +494,13 @@ class PyrexScanner(Scanner): ...@@ -495,16 +494,13 @@ class PyrexScanner(Scanner):
warning(useless_trailing_semicolon, "useless trailing semicolon") warning(useless_trailing_semicolon, "useless trailing semicolon")
def enable_keyword(self, name): def enable_keyword(self, name):
if name in self.keywords_stack: if name in self.keywords:
self.keywords_stack[name] += 1 return True # was enabled before
else: self.keywords.add(name)
self.keywords_stack[name] = 1 return False # was not enabled before
self.keywords.add(name)
def disable_keyword(self, name): def disable_keyword(self, name):
count = self.keywords_stack.get(name, 1) if name not in self.keywords:
if count == 1: return False # was not enabled before
self.keywords.discard(name) self.keywords.remove(name)
del self.keywords_stack[name] return True # was enabled before
else:
self.keywords_stack[name] = count - 1
...@@ -138,6 +138,17 @@ class TokenizerRegrTest(unittest.TestCase): ...@@ -138,6 +138,17 @@ class TokenizerRegrTest(unittest.TestCase):
self.assertEqual(type(ns['foo']()).__name__, 'coroutine') self.assertEqual(type(ns['foo']()).__name__, 'coroutine')
#self.assertTrue(inspect.iscoroutinefunction(ns['foo'])) #self.assertTrue(inspect.iscoroutinefunction(ns['foo']))
def test_syntax_async_await_as_names(self):
async def enable():
await 123
def disable():
await = 123
async = 'abc'
async def reenable():
await 432
class CoroutineTest(unittest.TestCase): class CoroutineTest(unittest.TestCase):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment