##// END OF EJS Templates
Merge pull request #13324 from GalBr/remove-pre-3.8-codepath-interactiveshell...
Matthias Bussonnier -
r27146:5d93becd merge
parent child Browse files
Show More
@@ -83,7 +83,7 b' from logging import error'
83 import IPython.core.hooks
83 import IPython.core.hooks
84
84
85 from typing import List as ListType, Tuple, Optional, Callable
85 from typing import List as ListType, Tuple, Optional, Callable
86 from ast import AST, stmt
86 from ast import stmt
87
87
88
88
89 # NoOpContext is deprecated, but ipykernel imports it from here.
89 # NoOpContext is deprecated, but ipykernel imports it from here.
@@ -112,13 +112,7 b' class ProvisionalWarning(DeprecationWarning):'
112 """
112 """
113 pass
113 pass
114
114
115 if sys.version_info > (3,8):
115 from ast import Module
116 from ast import Module
117 else :
118 # mock the new API, ignore second argument
119 # see https://github.com/ipython/ipython/issues/11590
120 from ast import Module as OriginalModule
121 Module = lambda nodelist, type_ignores: OriginalModule(nodelist)
122
116
123 _assign_nodes = (ast.AugAssign, ast.AnnAssign, ast.Assign)
117 _assign_nodes = (ast.AugAssign, ast.AnnAssign, ast.Assign)
124 _single_targets_nodes = (ast.AugAssign, ast.AnnAssign)
118 _single_targets_nodes = (ast.AugAssign, ast.AnnAssign)
@@ -127,104 +121,11 b' _single_targets_nodes = (ast.AugAssign, ast.AnnAssign)'
127 # Await Helpers
121 # Await Helpers
128 #-----------------------------------------------------------------------------
122 #-----------------------------------------------------------------------------
129
123
130 def removed_co_newlocals(function:types.FunctionType) -> types.FunctionType:
131 """Return a function that do not create a new local scope.
132
133 Given a function, create a clone of this function where the co_newlocal flag
134 has been removed, making this function code actually run in the sourounding
135 scope.
136
137 We need this in order to run asynchronous code in user level namespace.
138 """
139 from types import CodeType, FunctionType
140 CO_NEWLOCALS = 0x0002
141 code = function.__code__
142 new_co_flags = code.co_flags & ~CO_NEWLOCALS
143 if sys.version_info > (3, 8, 0, 'alpha', 3):
144 new_code = code.replace(co_flags=new_co_flags)
145 else:
146 new_code = CodeType(
147 code.co_argcount,
148 code.co_kwonlyargcount,
149 code.co_nlocals,
150 code.co_stacksize,
151 new_co_flags,
152 code.co_code,
153 code.co_consts,
154 code.co_names,
155 code.co_varnames,
156 code.co_filename,
157 code.co_name,
158 code.co_firstlineno,
159 code.co_lnotab,
160 code.co_freevars,
161 code.co_cellvars
162 )
163 return FunctionType(new_code, globals(), function.__name__, function.__defaults__)
164
165
166 # we still need to run things using the asyncio eventloop, but there is no
124 # we still need to run things using the asyncio eventloop, but there is no
167 # async integration
125 # async integration
168 from .async_helpers import (_asyncio_runner, _asyncify, _pseudo_sync_runner)
126 from .async_helpers import _asyncio_runner, _pseudo_sync_runner
169 from .async_helpers import _curio_runner, _trio_runner, _should_be_async
127 from .async_helpers import _curio_runner, _trio_runner, _should_be_async
170
128
171
172 def _ast_asyncify(cell:str, wrapper_name:str) -> ast.Module:
173 """
174 Parse a cell with top-level await and modify the AST to be able to run it later.
175
176 Parameters
177 ----------
178 cell: str
179 The code cell to asyncronify
180 wrapper_name: str
181 The name of the function to be used to wrap the passed `cell`. It is
182 advised to **not** use a python identifier in order to not pollute the
183 global namespace in which the function will be ran.
184
185 Returns
186 -------
187 ModuleType:
188 A module object AST containing **one** function named `wrapper_name`.
189
190 The given code is wrapped in a async-def function, parsed into an AST, and
191 the resulting function definition AST is modified to return the last
192 expression.
193
194 The last expression or await node is moved into a return statement at the
195 end of the function, and removed from its original location. If the last
196 node is not Expr or Await nothing is done.
197
198 The function `__code__` will need to be later modified (by
199 ``removed_co_newlocals``) in a subsequent step to not create new `locals()`
200 meaning that the local and global scope are the same, ie as if the body of
201 the function was at module level.
202
203 Lastly a call to `locals()` is made just before the last expression of the
204 function, or just after the last assignment or statement to make sure the
205 global dict is updated as python function work with a local fast cache which
206 is updated only on `local()` calls.
207 """
208
209 from ast import Expr, Await, Return, stmt, FunctionDef, Try, AsyncFunctionDef
210 if sys.version_info >= (3,8):
211 return ast.parse(cell)
212 tree = ast.parse(_asyncify(cell))
213
214 function_def = tree.body[0]
215 if sys.version_info > (3, 8):
216 assert isinstance(function_def, FunctionDef), function_def
217 else:
218 assert isinstance(function_def, (FunctionDef, AsyncFunctionDef)), function_def
219
220 function_def.name = wrapper_name
221 try_block = function_def.body[0]
222 assert isinstance(try_block, Try)
223 lastexpr = try_block.body[-1]
224 if isinstance(lastexpr, (Expr, Await)):
225 try_block.body[-1] = Return(lastexpr.value)
226 ast.fix_missing_locations(tree)
227 return tree
228 #-----------------------------------------------------------------------------
129 #-----------------------------------------------------------------------------
229 # Globals
130 # Globals
230 #-----------------------------------------------------------------------------
131 #-----------------------------------------------------------------------------
@@ -696,7 +597,6 b' class InteractiveShell(SingletonConfigurable):'
696 self.init_pdb()
597 self.init_pdb()
697 self.init_extension_manager()
598 self.init_extension_manager()
698 self.init_payload()
599 self.init_payload()
699 self.init_deprecation_warnings()
700 self.hooks.late_startup_hook()
600 self.hooks.late_startup_hook()
701 self.events.trigger('shell_initialized', self)
601 self.events.trigger('shell_initialized', self)
702 atexit.register(self.atexit_operations)
602 atexit.register(self.atexit_operations)
@@ -822,16 +722,6 b' class InteractiveShell(SingletonConfigurable):'
822 elif self.logstart:
722 elif self.logstart:
823 self.magic('logstart')
723 self.magic('logstart')
824
724
825 def init_deprecation_warnings(self):
826 """
827 register default filter for deprecation warning.
828
829 This will allow deprecation warning of function used interactively to show
830 warning to users, and still hide deprecation warning from libraries import.
831 """
832 if sys.version_info < (3,7):
833 warnings.filterwarnings("default", category=DeprecationWarning, module=self.user_ns.get("__name__"))
834
835
725
836 def init_builtins(self):
726 def init_builtins(self):
837 # A single, static flag that we set to True. Its presence indicates
727 # A single, static flag that we set to True. Its presence indicates
@@ -3153,28 +3043,7 b' class InteractiveShell(SingletonConfigurable):'
3153 with self.display_trap:
3043 with self.display_trap:
3154 # Compile to bytecode
3044 # Compile to bytecode
3155 try:
3045 try:
3156 if sys.version_info < (3,8) and self.autoawait:
3046 code_ast = compiler.ast_parse(cell, filename=cell_name)
3157 if _should_be_async(cell):
3158 # the code AST below will not be user code: we wrap it
3159 # in an `async def`. This will likely make some AST
3160 # transformer below miss some transform opportunity and
3161 # introduce a small coupling to run_code (in which we
3162 # bake some assumptions of what _ast_asyncify returns.
3163 # they are ways around (like grafting part of the ast
3164 # later:
3165 # - Here, return code_ast.body[0].body[1:-1], as well
3166 # as last expression in return statement which is
3167 # the user code part.
3168 # - Let it go through the AST transformers, and graft
3169 # - it back after the AST transform
3170 # But that seem unreasonable, at least while we
3171 # do not need it.
3172 code_ast = _ast_asyncify(cell, 'async-def-wrapper')
3173 _run_async = True
3174 else:
3175 code_ast = compiler.ast_parse(cell, filename=cell_name)
3176 else:
3177 code_ast = compiler.ast_parse(cell, filename=cell_name)
3178 except self.custom_exceptions as e:
3047 except self.custom_exceptions as e:
3179 etype, value, tb = sys.exc_info()
3048 etype, value, tb = sys.exc_info()
3180 self.CustomTB(etype, value, tb)
3049 self.CustomTB(etype, value, tb)
@@ -3200,8 +3069,6 b' class InteractiveShell(SingletonConfigurable):'
3200
3069
3201 # Execute the user code
3070 # Execute the user code
3202 interactivity = "none" if silent else self.ast_node_interactivity
3071 interactivity = "none" if silent else self.ast_node_interactivity
3203 if _run_async:
3204 interactivity = 'async'
3205
3072
3206 has_raised = await self.run_ast_nodes(code_ast.body, cell_name,
3073 has_raised = await self.run_ast_nodes(code_ast.body, cell_name,
3207 interactivity=interactivity, compiler=compiler, result=result)
3074 interactivity=interactivity, compiler=compiler, result=result)
@@ -3310,11 +3177,6 b' class InteractiveShell(SingletonConfigurable):'
3310 or the last assignment. Other values for this parameter will raise a
3177 or the last assignment. Other values for this parameter will raise a
3311 ValueError.
3178 ValueError.
3312
3179
3313 Experimental value: 'async' Will try to run top level interactive
3314 async/await code in default runner, this will not respect the
3315 interactivity setting and will only run the last node if it is an
3316 expression.
3317
3318 compiler : callable
3180 compiler : callable
3319 A function with the same interface as the built-in compile(), to turn
3181 A function with the same interface as the built-in compile(), to turn
3320 the AST nodes into code objects. Default is the built-in compile().
3182 the AST nodes into code objects. Default is the built-in compile().
@@ -3358,52 +3220,37 b' class InteractiveShell(SingletonConfigurable):'
3358 to_run_exec, to_run_interactive = nodelist[:-1], nodelist[-1:]
3220 to_run_exec, to_run_interactive = nodelist[:-1], nodelist[-1:]
3359 elif interactivity == 'all':
3221 elif interactivity == 'all':
3360 to_run_exec, to_run_interactive = [], nodelist
3222 to_run_exec, to_run_interactive = [], nodelist
3361 elif interactivity == 'async':
3362 to_run_exec, to_run_interactive = [], nodelist
3363 _async = True
3364 else:
3223 else:
3365 raise ValueError("Interactivity was %r" % interactivity)
3224 raise ValueError("Interactivity was %r" % interactivity)
3366
3225
3367 try:
3226 try:
3368 if _async and sys.version_info > (3,8):
3227
3369 raise ValueError("This branch should never happen on Python 3.8 and above, "
3228 def compare(code):
3370 "please try to upgrade IPython and open a bug report with your case.")
3229 is_async = inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
3371 if _async:
3230 return is_async
3372 # If interactivity is async the semantics of run_code are
3231
3373 # completely different Skip usual machinery.
3232 # refactor that to just change the mod constructor.
3374 mod = Module(nodelist, [])
3233 to_run = []
3375 async_wrapper_code = compiler(mod, cell_name, 'exec')
3234 for node in to_run_exec:
3376 exec(async_wrapper_code, self.user_global_ns, self.user_ns)
3235 to_run.append((node, "exec"))
3377 async_code = removed_co_newlocals(self.user_ns.pop('async-def-wrapper')).__code__
3236
3378 if (await self.run_code(async_code, result, async_=True)):
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(
3246 getattr(ast, "PyCF_ALLOW_TOP_LEVEL_AWAIT", 0x0)
3247 if self.autoawait
3248 else 0x0
3249 ):
3250 code = compiler(mod, cell_name, mode)
3251 asy = compare(code)
3252 if await self.run_code(code, result, async_=asy):
3379 return True
3253 return True
3380 else:
3381 if sys.version_info > (3, 8):
3382 def compare(code):
3383 is_async = (inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE)
3384 return is_async
3385 else:
3386 def compare(code):
3387 return _async
3388
3389 # refactor that to just change the mod constructor.
3390 to_run = []
3391 for node in to_run_exec:
3392 to_run.append((node, 'exec'))
3393
3394 for node in to_run_interactive:
3395 to_run.append((node, 'single'))
3396
3397 for node,mode in to_run:
3398 if mode == 'exec':
3399 mod = Module([node], [])
3400 elif mode == 'single':
3401 mod = ast.Interactive([node])
3402 with compiler.extra_flags(getattr(ast, 'PyCF_ALLOW_TOP_LEVEL_AWAIT', 0x0) if self.autoawait else 0x0):
3403 code = compiler(mod, cell_name, mode)
3404 asy = compare(code)
3405 if (await self.run_code(code, result, async_=asy)):
3406 return True
3407
3254
3408 # Flush softspace
3255 # Flush softspace
3409 if softspace(sys.stdout, 0):
3256 if softspace(sys.stdout, 0):
@@ -3426,21 +3273,6 b' class InteractiveShell(SingletonConfigurable):'
3426
3273
3427 return False
3274 return False
3428
3275
3429 def _async_exec(self, code_obj: types.CodeType, user_ns: dict):
3430 """
3431 Evaluate an asynchronous code object using a code runner
3432
3433 Fake asynchronous execution of code_object in a namespace via a proxy namespace.
3434
3435 Returns coroutine object, which can be executed via async loop runner
3436
3437 WARNING: The semantics of `async_exec` are quite different from `exec`,
3438 in particular you can only pass a single namespace. It also return a
3439 handle to the value of the last things returned by code_object.
3440 """
3441
3442 return eval(code_obj, user_ns)
3443
3444 async def run_code(self, code_obj, result=None, *, async_=False):
3276 async def run_code(self, code_obj, result=None, *, async_=False):
3445 """Execute a code object.
3277 """Execute a code object.
3446
3278
@@ -3475,11 +3307,7 b' class InteractiveShell(SingletonConfigurable):'
3475 try:
3307 try:
3476 try:
3308 try:
3477 self.hooks.pre_run_code_hook()
3309 self.hooks.pre_run_code_hook()
3478 if async_ and sys.version_info < (3,8):
3310 if async_:
3479 last_expr = (await self._async_exec(code_obj, self.user_ns))
3480 code = compile('last_expr', 'fake', "single")
3481 exec(code, {'last_expr': last_expr})
3482 elif async_ :
3483 await eval(code_obj, self.user_global_ns, self.user_ns)
3311 await eval(code_obj, self.user_global_ns, self.user_ns)
3484 else:
3312 else:
3485 exec(code_obj, self.user_global_ns, self.user_ns)
3313 exec(code_obj, self.user_global_ns, self.user_ns)
General Comments 0
You need to be logged in to leave comments. Login now