Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 212e059

Browse files
Merge pull request #158 from bcaller/await-transform
Transform all async ast nodes into sync nodes
2 parents 5b7d06b + de30591 commit 212e059

File tree

6 files changed

+90
-4
lines changed

6 files changed

+90
-4
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
async def g(x, *args):
2+
return await x()
3+
4+
5+
async def f(y):
6+
z = await g(y, await v)
7+
return z
8+
9+
10+
f(w)

‎pyt/core/ast_helper.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import subprocess
77
from functools import lru_cache
88

9+
from .transformer import AsyncTransformer
10+
911

1012
BLACK_LISTED_CALL_NAMES = ['self']
1113
recursive = False
@@ -32,7 +34,8 @@ def generate_ast(path):
3234
if os.path.isfile(path):
3335
with open(path, 'r') as f:
3436
try:
35-
return ast.parse(f.read())
37+
tree = ast.parse(f.read())
38+
return AsyncTransformer().visit(tree)
3639
except SyntaxError: # pragma: no cover
3740
global recursive
3841
if not recursive:

‎pyt/core/transformer.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import ast
2+
3+
4+
class AsyncTransformer(ast.NodeTransformer):
5+
"""Converts all async nodes into their synchronous counterparts."""
6+
7+
def visit_Await(self, node):
8+
"""Awaits are treated as if the keyword was absent."""
9+
return self.visit(node.value)
10+
11+
def visit_AsyncFunctionDef(self, node):
12+
return self.visit(ast.FunctionDef(**node.__dict__))
13+
14+
def visit_AsyncFor(self, node):
15+
return self.visit(ast.For(**node.__dict__))
16+
17+
def visit_AsyncWith(self, node):
18+
return self.visit(ast.With(**node.__dict__))

‎pyt/helper_visitors/vars_visitor.py‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ def visit_GeneratorComp(self, node):
6868
for gen in node.generators:
6969
self.comprehension(gen)
7070

71-
def visit_Await(self, node):
72-
self.visit(node.value)
73-
7471
def visit_Yield(self, node):
7572
if node.value:
7673
self.visit(node.value)

‎tests/cfg/cfg_test.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,28 @@ def test_name_constant_if(self):
14461446
self.assertEqual(expected_label, actual_label)
14471447

14481448

1449+
class CFGAsync(CFGBaseTestCase):
1450+
def test_await_keyword_treated_as_if_absent(self):
1451+
self.cfg_create_from_file('examples/example_inputs/asynchronous.py')
1452+
enter_g = 8
1453+
call_x = 9
1454+
ret_g = 10
1455+
exit_g = 11
1456+
call_ret_val = 12
1457+
set_z_to_g_ret_val = 13
1458+
1459+
for i in range(enter_g, set_z_to_g_ret_val + 1):
1460+
self.assertIn(self.cfg.nodes[i], self.cfg.nodes[i + 1].ingoing)
1461+
self.assertIn(self.cfg.nodes[i + 1], self.cfg.nodes[i].outgoing)
1462+
1463+
self.assertIsInstance(self.cfg.nodes[enter_g], EntryOrExitNode)
1464+
self.assertEqual(self.cfg.nodes[call_x].label, '~call_3 = ret_x()')
1465+
self.assertEqual(self.cfg.nodes[ret_g].label, 'ret_g = ~call_3')
1466+
self.assertIsInstance(self.cfg.nodes[exit_g], EntryOrExitNode)
1467+
self.assertEqual(self.cfg.nodes[call_ret_val].label, '~call_2 = ret_g')
1468+
self.assertEqual(self.cfg.nodes[set_z_to_g_ret_val].label, 'z = ~call_2')
1469+
1470+
14491471
class CFGName(CFGBaseTestCase):
14501472
"""Test is Name nodes are properly handled in different contexts"""
14511473

‎tests/core/transformer_test.py‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import ast
2+
import unittest
3+
4+
from pyt.core.transformer import AsyncTransformer
5+
6+
7+
class TransformerTest(unittest.TestCase):
8+
"""Tests for the AsyncTransformer."""
9+
10+
def test_async_removed_by_transformer(self):
11+
async_tree = ast.parse("\n".join([
12+
"async def a():",
13+
" async for b in c():",
14+
" await b()",
15+
" async with d() as e:",
16+
" pass",
17+
" return await y()"
18+
]))
19+
self.assertIsInstance(async_tree.body[0], ast.AsyncFunctionDef)
20+
self.assertIsInstance(async_tree.body[0].body[-1], ast.Return)
21+
self.assertIsInstance(async_tree.body[0].body[-1].value, ast.Await)
22+
23+
sync_tree = ast.parse("\n".join([
24+
"def a():",
25+
" for b in c():",
26+
" b()",
27+
" with d() as e:",
28+
" pass",
29+
" return y()"
30+
]))
31+
self.assertIsInstance(sync_tree.body[0], ast.FunctionDef)
32+
33+
transformed = AsyncTransformer().visit(async_tree)
34+
self.assertIsInstance(transformed.body[0], ast.FunctionDef)
35+
36+
self.assertEqual(ast.dump(transformed), ast.dump(sync_tree))

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /