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 |
|
|
|
3017 |
# - |
|
|
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 = |
|
|
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 ( |
|
|
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 |
|
|
|
3217 | code = compiler(mod, cell_name, "exec") | |
|
3218 | if (yield from self.run_code(code, result)): | |
|
3219 |
|
|
|
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 = ( |
|
|
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