Handle nested async for in for...

and Better async comprehension detection.

Still more work is needed. See commented-out section in
test/simple_source/bug37/02_async_for_generator.py
This commit is contained in:
rocky
2020-03-31 12:05:39 -04:00
parent af8add9df4
commit e2d349f781
6 changed files with 130 additions and 52 deletions

View File

@@ -6,3 +6,11 @@ def make_arange(n):
async def run(m):
return [i async for i in m]
# From 3.7.6 test_coroutines.py
async def run_list(pair, f):
return [i for pair in p async for i in f]
# FIXME: add this. It works in decompyle3
# async def run_gen():
# return (i async for i in f if 0 < i < 4)

View File

@@ -211,23 +211,51 @@ class Python37BaseParser(PythonParser):
if self.version < 3.8:
rules_str += """
stmt ::= async_with_stmt SETUP_ASYNC_WITH
c_stmt ::= c_async_with_stmt SETUP_ASYNC_WITH
async_with_stmt ::= expr
async_with_pre
POP_TOP
suite_stmts_opt
POP_BLOCK LOAD_CONST
async_with_post
c_async_with_stmt ::= expr
async_with_pre
POP_TOP
c_suite_stmts_opt
POP_BLOCK LOAD_CONST
async_with_post
async_with_stmt ::= expr
async_with_pre
POP_TOP
suite_stmts_opt
async_with_post
c_async_with_stmt ::= expr
async_with_pre
POP_TOP
c_suite_stmts_opt
async_with_post
async_with_as_stmt ::= expr
async_with_pre
store
suite_stmts_opt
POP_BLOCK LOAD_CONST
async_with_post
c_async_with_as_stmt ::= expr
async_with_pre
store
c_suite_stmts_opt
POP_BLOCK LOAD_CONST
async_with_post
async_with_as_stmt ::= expr
async_with_pre
store
suite_stmts_opt
async_with_post
c_async_with_as_stmt ::= expr
async_with_pre
store
suite_stmts_opt
async_with_post
"""
else:
rules_str += """
@@ -241,6 +269,12 @@ class Python37BaseParser(PythonParser):
suite_stmts
POP_TOP POP_BLOCK
async_with_post
c_async_with_stmt ::= expr
async_with_pre
POP_TOP
c_suite_stmts
POP_TOP POP_BLOCK
async_with_post
async_with_stmt ::= expr
async_with_pre
POP_TOP
@@ -252,15 +286,35 @@ class Python37BaseParser(PythonParser):
COME_FROM_ASYNC_WITH
WITH_CLEANUP_START GET_AWAITABLE LOAD_CONST YIELD_FROM
WITH_CLEANUP_FINISH END_FINALLY
c_async_with_stmt ::= expr
async_with_pre
POP_TOP
c_suite_stmts
POP_BLOCK
BEGIN_FINALLY
WITH_CLEANUP_START GET_AWAITABLE LOAD_CONST YIELD_FROM
WITH_CLEANUP_FINISH POP_FINALLY LOAD_CONST RETURN_VALUE
COME_FROM_ASYNC_WITH
WITH_CLEANUP_START GET_AWAITABLE LOAD_CONST YIELD_FROM
WITH_CLEANUP_FINISH END_FINALLY
async_with_as_stmt ::= expr
async_with_pre
store suite_stmts
POP_TOP POP_BLOCK
async_with_post
c_async_with_as_stmt ::= expr
async_with_pre
store suite_stmts
POP_TOP POP_BLOCK
async_with_post
async_with_as_stmt ::= expr
async_with_pre
store suite_stmts
POP_BLOCK async_with_post
c_async_with_as_stmt ::= expr
async_with_pre
store suite_stmts
POP_BLOCK async_with_post
"""
self.addRule(rules_str, nop_func)
@@ -539,7 +593,7 @@ class Python37BaseParser(PythonParser):
stmt ::= genexpr_func_async
func_async_prefix ::= SETUP_EXCEPT GET_ANEXT LOAD_CONST YIELD_FROM
func_async_prefix ::= _come_froms SETUP_EXCEPT GET_ANEXT LOAD_CONST YIELD_FROM
func_async_middle ::= POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT
DUP_TOP LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
END_FINALLY COME_FROM
@@ -548,22 +602,24 @@ class Python37BaseParser(PythonParser):
JUMP_BACK COME_FROM
POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP
expr ::= listcomp_async
listcomp_async ::= LOAD_LISTCOMP LOAD_STR MAKE_FUNCTION_0
expr ::= list_comp_async
list_comp_async ::= LOAD_LISTCOMP LOAD_STR MAKE_FUNCTION_0
expr GET_AITER CALL_FUNCTION_1
GET_AWAITABLE LOAD_CONST
YIELD_FROM
expr ::= listcomp_async
listcomp_async ::= BUILD_LIST_0 LOAD_FAST func_async_prefix
expr ::= list_comp_async
list_afor2 ::= func_async_prefix
store func_async_middle list_iter
JUMP_BACK COME_FROM
POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP
list_comp_async ::= BUILD_LIST_0 LOAD_FAST list_afor2
get_aiter ::= LOAD_DEREF GET_AITER
list_afor ::= get_aiter list_afor2
list_iter ::= list_afor
""",
nop_func,
)
custom_ops_processed.add(opname)
elif opname == "JUMP_IF_NOT_DEBUG":
v = token.attr
self.addRule(

View File

@@ -17,6 +17,7 @@
"""
from uncompyle6.semantics.consts import TABLE_DIRECT
from xdis.util import co_flags_is_async
from xdis.code import iscode
from uncompyle6.scanner import Code
@@ -87,7 +88,9 @@ def customize_for_version3(self, version):
p = self.prec
self.prec = 27
code = Code(node[1].attr, self.scanner, self.currentclass)
code_obj = node[1].attr
assert iscode(code_obj)
code = Code(code_obj, self.scanner, self.currentclass)
ast = self.build_ast(code._tokens, code._customize)
self.customize(code._customize)
@@ -141,7 +144,10 @@ def customize_for_version3(self, version):
# Find the list comprehension body. It is the inner-most
# node that is not list_.. .
while n == "list_iter":
n = n[0] # recurse one step
# recurse one step
n = n[0]
if n == "list_for":
stores.append(n[2])
n = n[3]
@@ -168,6 +174,11 @@ def customize_for_version3(self, version):
list_ifs.append(n)
n = n[-1]
pass
elif n == "list_afor":
collections.append(n[0][0])
n = n[1]
stores.append(n[1][0])
n = n[3]
pass
assert n == "lc_body", ast
@@ -175,7 +186,13 @@ def customize_for_version3(self, version):
self.preorder(n[0])
# FIXME: add indentation around "for"'s and "in"'s
n_colls = len(collections)
for i, store in enumerate(stores):
if i >= n_colls:
break
if collections[i] == "LOAD_DEREF" and co_flags_is_async(code_obj.co_flags):
self.write(" async")
pass
self.write(" for ")
self.preorder(store)
self.write(" in ")

View File

@@ -16,7 +16,7 @@
"""
from xdis.code import iscode
from xdis.util import COMPILER_FLAG_BIT
from xdis.util import co_flags_is_async
from uncompyle6.semantics.consts import (
INDENT_PER_LEVEL,
PRECEDENCE,
@@ -207,14 +207,7 @@ def customize_for_version35(self, version):
pass
is_code = hasattr(code_node, "attr") and iscode(code_node.attr)
return is_code and (
code_node.attr.co_flags
& (
COMPILER_FLAG_BIT["COROUTINE"]
| COMPILER_FLAG_BIT["ITERABLE_COROUTINE"]
| COMPILER_FLAG_BIT["ASYNC_GENERATOR"]
)
)
return is_code and co_flags_is_async(code_node.attr.co_flags)
def n_function_def(node):
if is_async_fn(node):

View File

@@ -1207,7 +1207,11 @@ class SourceWalker(GenericASTTraversal, object):
is_30_dict_comp = False
store = None
if node == "list_comp_async":
n = ast[2][1]
else:
n = ast[iter_index]
if ast in (
"set_comp_func",
"dict_comp_func",
@@ -1238,7 +1242,7 @@ class SourceWalker(GenericASTTraversal, object):
pass
pass
elif ast == "listcomp_async":
store = ast[3]
store = ast[2][1]
else:
assert n == "list_iter", n