##// END OF EJS Templates
Merge pull request #13326 from Carreau/clean-ast-asyncify...
Matthias Bussonnier -
r27151:a9dac047 merge
parent child Browse files
Show More
@@ -12,13 +12,10 b' Python semantics.'
12
12
13
13
14 import ast
14 import ast
15 import sys
16 import inspect
15 import inspect
17 from textwrap import dedent, indent
18
16
19
17
20 class _AsyncIORunner:
18 class _AsyncIORunner:
21
22 def __call__(self, coro):
19 def __call__(self, coro):
23 """
20 """
24 Handler for asyncio autoawait
21 Handler for asyncio autoawait
@@ -28,7 +25,8 b' class _AsyncIORunner:'
28 return asyncio.get_event_loop_policy().get_event_loop().run_until_complete(coro)
25 return asyncio.get_event_loop_policy().get_event_loop().run_until_complete(coro)
29
26
30 def __str__(self):
27 def __str__(self):
31 return 'asyncio'
28 return "asyncio"
29
32
30
33 _asyncio_runner = _AsyncIORunner()
31 _asyncio_runner = _AsyncIORunner()
34
32
@@ -74,69 +72,6 b' def _pseudo_sync_runner(coro):'
74 )
72 )
75
73
76
74
77 def _asyncify(code: str) -> str:
78 """wrap code in async def definition.
79
80 And setup a bit of context to run it later.
81 """
82 res = dedent(
83 """
84 async def __wrapper__():
85 try:
86 {usercode}
87 finally:
88 locals()
89 """
90 ).format(usercode=indent(code, " " * 8))
91 return res
92
93
94 class _AsyncSyntaxErrorVisitor(ast.NodeVisitor):
95 """
96 Find syntax errors that would be an error in an async repl, but because
97 the implementation involves wrapping the repl in an async function, it
98 is erroneously allowed (e.g. yield or return at the top level)
99 """
100 def __init__(self):
101 if sys.version_info >= (3,8):
102 raise ValueError('DEPRECATED in Python 3.8+')
103 self.depth = 0
104 super().__init__()
105
106 def generic_visit(self, node):
107 func_types = (ast.FunctionDef, ast.AsyncFunctionDef)
108 invalid_types_by_depth = {
109 0: (ast.Return, ast.Yield, ast.YieldFrom),
110 1: (ast.Nonlocal,)
111 }
112
113 should_traverse = self.depth < max(invalid_types_by_depth.keys())
114 if isinstance(node, func_types) and should_traverse:
115 self.depth += 1
116 super().generic_visit(node)
117 self.depth -= 1
118 elif isinstance(node, invalid_types_by_depth[self.depth]):
119 raise SyntaxError()
120 else:
121 super().generic_visit(node)
122
123
124 def _async_parse_cell(cell: str) -> ast.AST:
125 """
126 This is a compatibility shim for pre-3.7 when async outside of a function
127 is a syntax error at the parse stage.
128
129 It will return an abstract syntax tree parsed as if async and await outside
130 of a function were not a syntax error.
131 """
132 if sys.version_info < (3, 7):
133 # Prior to 3.7 you need to asyncify before parse
134 wrapped_parse_tree = ast.parse(_asyncify(cell))
135 return wrapped_parse_tree.body[0].body[0]
136 else:
137 return ast.parse(cell)
138
139
140 def _should_be_async(cell: str) -> bool:
75 def _should_be_async(cell: str) -> bool:
141 """Detect if a block of code need to be wrapped in an `async def`
76 """Detect if a block of code need to be wrapped in an `async def`
142
77
@@ -148,25 +83,10 b' def _should_be_async(cell: str) -> bool:'
148 Not handled yet: If the block of code has a return statement as the top
83 Not handled yet: If the block of code has a return statement as the top
149 level, it will be seen as async. This is a know limitation.
84 level, it will be seen as async. This is a know limitation.
150 """
85 """
151 if sys.version_info > (3, 8):
152 try:
86 try:
153 code = compile(cell, "<>", "exec", flags=getattr(ast,'PyCF_ALLOW_TOP_LEVEL_AWAIT', 0x0))
87 code = compile(
88 cell, "<>", "exec", flags=getattr(ast, "PyCF_ALLOW_TOP_LEVEL_AWAIT", 0x0)
89 )
154 return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
90 return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
155 except (SyntaxError, MemoryError):
91 except (SyntaxError, MemoryError):
156 return False
92 return False
157 try:
158 # we can't limit ourself to ast.parse, as it __accepts__ to parse on
159 # 3.7+, but just does not _compile_
160 code = compile(cell, "<>", "exec")
161 except (SyntaxError, MemoryError):
162 try:
163 parse_tree = _async_parse_cell(cell)
164
165 # Raise a SyntaxError if there are top-level return or yields
166 v = _AsyncSyntaxErrorVisitor()
167 v.visit(parse_tree)
168
169 except (SyntaxError, MemoryError):
170 return False
171 return True
172 return False
General Comments 0
You need to be logged in to leave comments. Login now