##// END OF EJS Templates
Merge pull request #11713 from Carreau/async-exec...
Matthias Bussonnier -
r25087:0e6f90ec merge
parent child Browse files
Show More
@@ -13,6 +13,7 b' Python semantics.'
13
13
14 import ast
14 import ast
15 import sys
15 import sys
16 import inspect
16 from textwrap import dedent, indent
17 from textwrap import dedent, indent
17
18
18
19
@@ -98,6 +99,8 b' class _AsyncSyntaxErrorVisitor(ast.NodeVisitor):'
98 is erroneously allowed (e.g. yield or return at the top level)
99 is erroneously allowed (e.g. yield or return at the top level)
99 """
100 """
100 def __init__(self):
101 def __init__(self):
102 if sys.version_info >= (3,8):
103 raise ValueError('DEPRECATED in Python 3.8+')
101 self.depth = 0
104 self.depth = 0
102 super().__init__()
105 super().__init__()
103
106
@@ -146,12 +149,16 b' def _should_be_async(cell: str) -> bool:'
146 Not handled yet: If the block of code has a return statement as the top
149 Not handled yet: If the block of code has a return statement as the top
147 level, it will be seen as async. This is a know limitation.
150 level, it will be seen as async. This is a know limitation.
148 """
151 """
149
152 if sys.version_info > (3, 8):
153 try:
154 code = compile(cell, "<>", "exec", flags=getattr(ast,'PyCF_ALLOW_TOP_LEVEL_AWAIT', 0x0))
155 return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
156 except SyntaxError:
157 return False
150 try:
158 try:
151 # we can't limit ourself to ast.parse, as it __accepts__ to parse on
159 # we can't limit ourself to ast.parse, as it __accepts__ to parse on
152 # 3.7+, but just does not _compile_
160 # 3.7+, but just does not _compile_
153 compile(cell, "<>", "exec")
161 code = compile(cell, "<>", "exec")
154 return False
155 except SyntaxError:
162 except SyntaxError:
156 try:
163 try:
157 parse_tree = _async_parse_cell(cell)
164 parse_tree = _async_parse_cell(cell)
@@ -35,6 +35,7 b' import hashlib'
35 import linecache
35 import linecache
36 import operator
36 import operator
37 import time
37 import time
38 from contextlib import contextmanager
38
39
39 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
40 # Constants
41 # Constants
@@ -134,6 +135,21 b' class CachingCompiler(codeop.Compile):'
134 linecache._ipython_cache[name] = entry
135 linecache._ipython_cache[name] = entry
135 return name
136 return name
136
137
138 @contextmanager
139 def extra_flags(self, flags):
140 ## bits that we'll set to 1
141 turn_on_bits = ~self.flags & flags
142
143
144 self.flags = self.flags | flags
145 try:
146 yield
147 finally:
148 # turn off only the bits we turned on so that something like
149 # __future__ that set flags stays.
150 self.flags &= ~turn_on_bits
151
152
137 def check_linecache_ipython(*args):
153 def check_linecache_ipython(*args):
138 """Call linecache.checkcache() safely protecting our cached values.
154 """Call linecache.checkcache() safely protecting our cached values.
139 """
155 """
@@ -17,6 +17,7 b' import asyncio'
17 import atexit
17 import atexit
18 import builtins as builtin_mod
18 import builtins as builtin_mod
19 import functools
19 import functools
20 import inspect
20 import os
21 import os
21 import re
22 import re
22 import runpy
23 import runpy
@@ -165,7 +166,6 b' def removed_co_newlocals(function:types.FunctionType) -> types.FunctionType:'
165 # we still need to run things using the asyncio eventloop, but there is no
166 # we still need to run things using the asyncio eventloop, but there is no
166 # async integration
167 # async integration
167 from .async_helpers import (_asyncio_runner, _asyncify, _pseudo_sync_runner)
168 from .async_helpers import (_asyncio_runner, _asyncify, _pseudo_sync_runner)
168
169 if sys.version_info > (3, 5):
169 if sys.version_info > (3, 5):
170 from .async_helpers import _curio_runner, _trio_runner, _should_be_async
170 from .async_helpers import _curio_runner, _trio_runner, _should_be_async
171 else :
171 else :
@@ -214,6 +214,8 b' def _ast_asyncify(cell:str, wrapper_name:str) -> ast.Module:'
214 """
214 """
215
215
216 from ast import Expr, Await, Return
216 from ast import Expr, Await, Return
217 if sys.version_info >= (3,8):
218 return ast.parse(cell)
217 tree = ast.parse(_asyncify(cell))
219 tree = ast.parse(_asyncify(cell))
218
220
219 function_def = tree.body[0]
221 function_def = tree.body[0]
@@ -2909,8 +2911,7 b' class InteractiveShell(SingletonConfigurable):'
2909 return False
2911 return False
2910 return _should_be_async(cell)
2912 return _should_be_async(cell)
2911
2913
2912 @asyncio.coroutine
2914 async def run_cell_async(self, raw_cell: str, store_history=False, silent=False, shell_futures=True) -> ExecutionResult:
2913 def run_cell_async(self, raw_cell: str, store_history=False, silent=False, shell_futures=True) -> ExecutionResult:
2914 """Run a complete IPython cell asynchronously.
2915 """Run a complete IPython cell asynchronously.
2915
2916
2916 Parameters
2917 Parameters
@@ -3002,23 +3003,26 b' class InteractiveShell(SingletonConfigurable):'
3002 with self.display_trap:
3003 with self.display_trap:
3003 # Compile to bytecode
3004 # Compile to bytecode
3004 try:
3005 try:
3005 if self.autoawait and _should_be_async(cell):
3006 if sys.version_info < (3,8) and self.autoawait:
3006 # the code AST below will not be user code: we wrap it
3007 if _should_be_async(cell):
3007 # in an `async def`. This will likely make some AST
3008 # the code AST below will not be user code: we wrap it
3008 # transformer below miss some transform opportunity and
3009 # in an `async def`. This will likely make some AST
3009 # introduce a small coupling to run_code (in which we
3010 # transformer below miss some transform opportunity and
3010 # bake some assumptions of what _ast_asyncify returns.
3011 # introduce a small coupling to run_code (in which we
3011 # they are ways around (like grafting part of the ast
3012 # bake some assumptions of what _ast_asyncify returns.
3012 # later:
3013 # they are ways around (like grafting part of the ast
3013 # - Here, return code_ast.body[0].body[1:-1], as well
3014 # later:
3014 # as last expression in return statement which is
3015 # - Here, return code_ast.body[0].body[1:-1], as well
3015 # the user code part.
3016 # as last expression in return statement which is
3016 # - Let it go through the AST transformers, and graft
3017 # the user code part.
3017 # - it back after the AST transform
3018 # - Let it go through the AST transformers, and graft
3018 # But that seem unreasonable, at least while we
3019 # - it back after the AST transform
3019 # do not need it.
3020 # But that seem unreasonable, at least while we
3020 code_ast = _ast_asyncify(cell, 'async-def-wrapper')
3021 # do not need it.
3021 _run_async = True
3022 code_ast = _ast_asyncify(cell, 'async-def-wrapper')
3023 _run_async = True
3024 else:
3025 code_ast = compiler.ast_parse(cell, filename=cell_name)
3022 else:
3026 else:
3023 code_ast = compiler.ast_parse(cell, filename=cell_name)
3027 code_ast = compiler.ast_parse(cell, filename=cell_name)
3024 except self.custom_exceptions as e:
3028 except self.custom_exceptions as e:
@@ -3049,7 +3053,7 b' class InteractiveShell(SingletonConfigurable):'
3049 if _run_async:
3053 if _run_async:
3050 interactivity = 'async'
3054 interactivity = 'async'
3051
3055
3052 has_raised = yield from self.run_ast_nodes(code_ast.body, cell_name,
3056 has_raised = await self.run_ast_nodes(code_ast.body, cell_name,
3053 interactivity=interactivity, compiler=compiler, result=result)
3057 interactivity=interactivity, compiler=compiler, result=result)
3054
3058
3055 self.last_execution_succeeded = not has_raised
3059 self.last_execution_succeeded = not has_raised
@@ -3129,8 +3133,7 b' class InteractiveShell(SingletonConfigurable):'
3129 ast.fix_missing_locations(node)
3133 ast.fix_missing_locations(node)
3130 return node
3134 return node
3131
3135
3132 @asyncio.coroutine
3136 async def run_ast_nodes(self, nodelist:ListType[AST], cell_name:str, interactivity='last_expr',
3133 def run_ast_nodes(self, nodelist:ListType[AST], cell_name:str, interactivity='last_expr',
3134 compiler=compile, result=None):
3137 compiler=compile, result=None):
3135 """Run a sequence of AST nodes. The execution mode depends on the
3138 """Run a sequence of AST nodes. The execution mode depends on the
3136 interactivity parameter.
3139 interactivity parameter.
@@ -3169,6 +3172,7 b' class InteractiveShell(SingletonConfigurable):'
3169 """
3172 """
3170 if not nodelist:
3173 if not nodelist:
3171 return
3174 return
3175
3172 if interactivity == 'last_expr_or_assign':
3176 if interactivity == 'last_expr_or_assign':
3173 if isinstance(nodelist[-1], _assign_nodes):
3177 if isinstance(nodelist[-1], _assign_nodes):
3174 asg = nodelist[-1]
3178 asg = nodelist[-1]
@@ -3198,10 +3202,15 b' class InteractiveShell(SingletonConfigurable):'
3198 elif interactivity == 'all':
3202 elif interactivity == 'all':
3199 to_run_exec, to_run_interactive = [], nodelist
3203 to_run_exec, to_run_interactive = [], nodelist
3200 elif interactivity == 'async':
3204 elif interactivity == 'async':
3205 to_run_exec, to_run_interactive = [], nodelist
3201 _async = True
3206 _async = True
3202 else:
3207 else:
3203 raise ValueError("Interactivity was %r" % interactivity)
3208 raise ValueError("Interactivity was %r" % interactivity)
3209
3204 try:
3210 try:
3211 if _async and sys.version_info > (3,8):
3212 raise ValueError("This branch should never happen on Python 3.8 and above, "
3213 "please try to upgrade IPython and open a bug report with your case.")
3205 if _async:
3214 if _async:
3206 # If interactivity is async the semantics of run_code are
3215 # If interactivity is async the semantics of run_code are
3207 # completely different Skip usual machinery.
3216 # completely different Skip usual machinery.
@@ -3209,19 +3218,34 b' class InteractiveShell(SingletonConfigurable):'
3209 async_wrapper_code = compiler(mod, cell_name, 'exec')
3218 async_wrapper_code = compiler(mod, cell_name, 'exec')
3210 exec(async_wrapper_code, self.user_global_ns, self.user_ns)
3219 exec(async_wrapper_code, self.user_global_ns, self.user_ns)
3211 async_code = removed_co_newlocals(self.user_ns.pop('async-def-wrapper')).__code__
3220 async_code = removed_co_newlocals(self.user_ns.pop('async-def-wrapper')).__code__
3212 if (yield from self.run_code(async_code, result, async_=True)):
3221 if (await self.run_code(async_code, result, async_=True)):
3213 return True
3222 return True
3214 else:
3223 else:
3215 for i, node in enumerate(to_run_exec):
3224 if sys.version_info > (3, 8):
3216 mod = Module([node], [])
3225 def compare(code):
3217 code = compiler(mod, cell_name, "exec")
3226 is_async = (inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE)
3218 if (yield from self.run_code(code, result)):
3227 return is_async
3219 return True
3228 else:
3220
3229 def compare(code):
3221 for i, node in enumerate(to_run_interactive):
3230 return _async
3222 mod = ast.Interactive([node])
3231
3223 code = compiler(mod, cell_name, "single")
3232 # refactor that to just change the mod constructor.
3224 if (yield from self.run_code(code, result)):
3233 to_run = []
3234 for node in to_run_exec:
3235 to_run.append((node, 'exec'))
3236
3237 for node in to_run_interactive:
3238 to_run.append((node, 'single'))
3239
3240 for node,mode in to_run:
3241 if mode == 'exec':
3242 mod = Module([node], [])
3243 elif mode == 'single':
3244 mod = ast.Interactive([node])
3245 with compiler.extra_flags(getattr(ast, 'PyCF_ALLOW_TOP_LEVEL_AWAIT', 0x0) if self.autoawait else 0x0):
3246 code = compiler(mod, cell_name, mode)
3247 asy = compare(code)
3248 if (await self.run_code(code, result, async_=asy)):
3225 return True
3249 return True
3226
3250
3227 # Flush softspace
3251 # Flush softspace
@@ -3260,8 +3284,7 b' class InteractiveShell(SingletonConfigurable):'
3260
3284
3261 return eval(code_obj, user_ns)
3285 return eval(code_obj, user_ns)
3262
3286
3263 @asyncio.coroutine
3287 async def run_code(self, code_obj, result=None, *, async_=False):
3264 def run_code(self, code_obj, result=None, *, async_=False):
3265 """Execute a code object.
3288 """Execute a code object.
3266
3289
3267 When an exception occurs, self.showtraceback() is called to display a
3290 When an exception occurs, self.showtraceback() is called to display a
@@ -3292,10 +3315,12 b' class InteractiveShell(SingletonConfigurable):'
3292 try:
3315 try:
3293 try:
3316 try:
3294 self.hooks.pre_run_code_hook()
3317 self.hooks.pre_run_code_hook()
3295 if async_:
3318 if async_ and sys.version_info < (3,8):
3296 last_expr = (yield from self._async_exec(code_obj, self.user_ns))
3319 last_expr = (await self._async_exec(code_obj, self.user_ns))
3297 code = compile('last_expr', 'fake', "single")
3320 code = compile('last_expr', 'fake', "single")
3298 exec(code, {'last_expr': last_expr})
3321 exec(code, {'last_expr': last_expr})
3322 elif async_ :
3323 await eval(code_obj, self.user_global_ns, self.user_ns)
3299 else:
3324 else:
3300 exec(code_obj, self.user_global_ns, self.user_ns)
3325 exec(code_obj, self.user_global_ns, self.user_ns)
3301 finally:
3326 finally:
General Comments 0
You need to be logged in to leave comments. Login now