##// 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 14 import ast
15 import sys
16 15 import inspect
17 from textwrap import dedent, indent
18 16
19 17
20 18 class _AsyncIORunner:
21
22 19 def __call__(self, coro):
23 20 """
24 21 Handler for asyncio autoawait
@@ -28,7 +25,8 b' class _AsyncIORunner:'
28 25 return asyncio.get_event_loop_policy().get_event_loop().run_until_complete(coro)
29 26
30 27 def __str__(self):
31 return 'asyncio'
28 return "asyncio"
29
32 30
33 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 75 def _should_be_async(cell: str) -> bool:
141 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 83 Not handled yet: If the block of code has a return statement as the top
149 84 level, it will be seen as async. This is a know limitation.
150 85 """
151 if sys.version_info > (3, 8):
152 try:
153 code = compile(cell, "<>", "exec", flags=getattr(ast,'PyCF_ALLOW_TOP_LEVEL_AWAIT', 0x0))
154 return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
155 except (SyntaxError, MemoryError):
156 return False
157 86 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")
87 code = compile(
88 cell, "<>", "exec", flags=getattr(ast, "PyCF_ALLOW_TOP_LEVEL_AWAIT", 0x0)
89 )
90 return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
161 91 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
92 return False
General Comments 0
You need to be logged in to leave comments. Login now