You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
5.7 KiB
132 lines
5.7 KiB
# Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
|
|
|
from pydevd_asyncio_constants import IS_ASYNCIO_DEBUGGER_ENV
|
|
from _pydevd_bundle.pydevd_exec2 import Exec
|
|
from _pydev_bundle.pydev_log import warn
|
|
|
|
|
|
eval_async_expression_in_context = None
|
|
eval_async_expression = None
|
|
exec_async_code = None
|
|
asyncio_command_compiler = None
|
|
|
|
if IS_ASYNCIO_DEBUGGER_ENV:
|
|
from _pydevd_bundle import pydevd_save_locals
|
|
from pydevd_nest_asyncio import apply, PyDevCoro
|
|
from codeop import CommandCompiler
|
|
import ast, types, inspect, asyncio
|
|
|
|
FILENAME = '<string>'
|
|
EVAL_SYMBOL = 'eval'
|
|
EXEC_SYMBOL = 'exec'
|
|
MODULE = '<module>'
|
|
|
|
if apply():
|
|
_asyncio_command_compiler = CommandCompiler()
|
|
_asyncio_command_compiler.compiler.flags |= ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
|
|
|
|
|
|
def _compile_async_expression(expression, do_exec):
|
|
"""
|
|
Compile an expression with 'eval' or 'exec' compilation flag.\n
|
|
Can compile an expression with `await` outside function.
|
|
|
|
:param str expression: compilation target
|
|
:param bool do_exec: if True then compilation flag is 'exec' else 'eval'
|
|
:return: a pair of compilation result and compilation flag
|
|
:raises (OverflowError, SyntaxError, ValueError): if compilation failed
|
|
"""
|
|
compilation_flag = EVAL_SYMBOL
|
|
if do_exec:
|
|
try:
|
|
compiled = asyncio_command_compiler(expression, FILENAME, compilation_flag)
|
|
except (OverflowError, SyntaxError, ValueError):
|
|
compilation_flag = EXEC_SYMBOL
|
|
compiled = asyncio_command_compiler(expression, FILENAME, compilation_flag)
|
|
else:
|
|
compiled = asyncio_command_compiler(expression, FILENAME, compilation_flag)
|
|
return compiled, compilation_flag
|
|
|
|
|
|
def _eval_async_expression_in_context(expression, global_names, local_names, do_exec):
|
|
"""
|
|
Compile an expression and if the compilation result is coroutine then put it in asyncio event loop else evaluate.\n
|
|
Can evaluate an expression with `await` outside function.
|
|
|
|
:param str expression: evaluation target
|
|
:param global_names: the dictionary implementing the current module namespace
|
|
:param local_names: the dictionary representing the current local symbol table
|
|
:param bool do_exec: if True then the compilation flag is 'exec' else 'eval'
|
|
:return: evaluation result
|
|
:raises (OverflowError, SyntaxError, ValueError): if a compilation failed
|
|
"""
|
|
apply()
|
|
updated_globals = {}
|
|
updated_globals.update(global_names)
|
|
updated_globals.update(local_names)
|
|
|
|
compiled, _ = _compile_async_expression(expression, do_exec)
|
|
return exec_async_code(compiled, updated_globals)
|
|
|
|
|
|
def _eval_async_expression(expression, global_names, frame, do_exec, exception_handler):
|
|
"""
|
|
Compile an expression and if the compilation result is coroutine then put it in asyncio event loop else evaluate.\n
|
|
Can evaluate an expression with `await` outside function.
|
|
|
|
:param str expression: evaluation target
|
|
:param global_names: the dictionary implementing the current module namespace and the current local symbol table
|
|
:param frame: the current frame
|
|
:param bool do_exec: if True then the compilation flag is 'exec' else 'eval'
|
|
:param exception_handler: handle an exception thrown at compile time
|
|
:return: evaluation result or exception string
|
|
"""
|
|
apply()
|
|
locals = frame.f_locals
|
|
try:
|
|
compiled, compilation_flag = _compile_async_expression(expression, do_exec)
|
|
if compiled is None:
|
|
try:
|
|
compile(expression, FILENAME, compilation_flag, asyncio_command_compiler.compiler.flags)
|
|
except (OverflowError, SyntaxError, ValueError):
|
|
return exception_handler(expression, locals)
|
|
result = exec_async_code(compiled, global_names)
|
|
if compilation_flag == EXEC_SYMBOL:
|
|
Exec(expression, global_names, frame.f_locals)
|
|
pydevd_save_locals.save_locals(frame)
|
|
return result
|
|
except (OverflowError, SyntaxError, ValueError):
|
|
return exception_handler(expression, locals)
|
|
|
|
|
|
def _exec_async_code(code, global_names):
|
|
"""
|
|
If code is coroutine then put it in an asyncio event loop else evaluate
|
|
|
|
:param code: evaluation target
|
|
:param global_names: the dictionary implementing the current module namespace
|
|
:return: evaluation result
|
|
"""
|
|
|
|
try:
|
|
apply()
|
|
except:
|
|
warn('Failed to patch asyncio')
|
|
|
|
func = types.FunctionType(code, global_names)
|
|
result = func()
|
|
try:
|
|
if inspect.iscoroutine(result) and MODULE in str(result):
|
|
loop = asyncio.get_event_loop()
|
|
result = loop.run_until_complete(PyDevCoro(result))
|
|
except:
|
|
warn('Failed to run coroutine %s' % str(result))
|
|
finally:
|
|
return result
|
|
|
|
|
|
eval_async_expression_in_context = _eval_async_expression_in_context
|
|
eval_async_expression = _eval_async_expression
|
|
exec_async_code = _exec_async_code
|
|
asyncio_command_compiler = _asyncio_command_compiler
|