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,7 +3003,8 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: | |
|
3007 | if _should_be_async(cell): | |||
3006 | # the code AST below will not be user code: we wrap it |
|
3008 | # the code AST below will not be user code: we wrap it | |
3007 | # in an `async def`. This will likely make some AST |
|
3009 | # in an `async def`. This will likely make some AST | |
3008 | # transformer below miss some transform opportunity and |
|
3010 | # transformer below miss some transform opportunity and | |
@@ -3021,6 +3023,8 b' class InteractiveShell(SingletonConfigurable):' | |||||
3021 | _run_async = True |
|
3023 | _run_async = True | |
3022 | else: |
|
3024 | else: | |
3023 | code_ast = compiler.ast_parse(cell, filename=cell_name) |
|
3025 | code_ast = compiler.ast_parse(cell, filename=cell_name) | |
|
3026 | else: | |||
|
3027 | code_ast = compiler.ast_parse(cell, filename=cell_name) | |||
3024 | except self.custom_exceptions as e: |
|
3028 | except self.custom_exceptions as e: | |
3025 | etype, value, tb = sys.exc_info() |
|
3029 | etype, value, tb = sys.exc_info() | |
3026 | self.CustomTB(etype, value, tb) |
|
3030 | self.CustomTB(etype, value, tb) | |
@@ -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 = |
|
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 ( |
|
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 |
|
|
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 |
|
|
3228 | else: | |
|
3229 | def compare(code): | |||
|
3230 | return _async | |||
3220 |
|
3231 | |||
3221 | for i, node in enumerate(to_run_interactive): |
|
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': | |||
3222 | mod = ast.Interactive([node]) |
|
3244 | mod = ast.Interactive([node]) | |
3223 | code = compiler(mod, cell_name, "single") |
|
3245 | with compiler.extra_flags(getattr(ast, 'PyCF_ALLOW_TOP_LEVEL_AWAIT', 0x0) if self.autoawait else 0x0): | |
3224 | if (yield from self.run_code(code, result)): |
|
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 = ( |
|
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