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