mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-05 01:50:56 +07:00
IDEA-CR-63337: PY-40019 Make Console commands execution visible for users and properly report exceptions from Python side inside IDE
GitOrigin-RevId: 9bdcff1fc822dea47d6e23192920dec5c452cd04
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2647ec2735
commit
b1936886a0
@@ -7,7 +7,7 @@
|
||||
package com.jetbrains.python.console.protocol;
|
||||
|
||||
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class ArrayData implements org.apache.thrift.TBase<ArrayData, ArrayData._Fields>, java.io.Serializable, Cloneable, Comparable<ArrayData> {
|
||||
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("ArrayData");
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
package com.jetbrains.python.console.protocol;
|
||||
|
||||
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class ArrayHeaders implements org.apache.thrift.TBase<ArrayHeaders, ArrayHeaders._Fields>, java.io.Serializable, Cloneable, Comparable<ArrayHeaders> {
|
||||
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("ArrayHeaders");
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
package com.jetbrains.python.console.protocol;
|
||||
|
||||
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class ColHeader implements org.apache.thrift.TBase<ColHeader, ColHeader._Fields>, java.io.Serializable, Cloneable, Comparable<ColHeader> {
|
||||
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("ColHeader");
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
package com.jetbrains.python.console.protocol;
|
||||
|
||||
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class CompletionOption implements org.apache.thrift.TBase<CompletionOption, CompletionOption._Fields>, java.io.Serializable, Cloneable, Comparable<CompletionOption> {
|
||||
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("CompletionOption");
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ package com.jetbrains.python.console.protocol;
|
||||
/**
|
||||
* Corresponds to `PyDebugValue`.
|
||||
*/
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class DebugValue implements org.apache.thrift.TBase<DebugValue, DebugValue._Fields>, java.io.Serializable, Cloneable, Comparable<DebugValue> {
|
||||
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("DebugValue");
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ package com.jetbrains.python.console.protocol;
|
||||
* Indicates that the related array has more than two dimensions.
|
||||
*
|
||||
*/
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class ExceedingArrayDimensionsException extends org.apache.thrift.TException implements org.apache.thrift.TBase<ExceedingArrayDimensionsException, ExceedingArrayDimensionsException._Fields>, java.io.Serializable, Cloneable, Comparable<ExceedingArrayDimensionsException> {
|
||||
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("ExceedingArrayDimensionsException");
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ package com.jetbrains.python.console.protocol;
|
||||
* Corresponds to `ArrayChunk`.
|
||||
*
|
||||
*/
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class GetArrayResponse implements org.apache.thrift.TBase<GetArrayResponse, GetArrayResponse._Fields>, java.io.Serializable, Cloneable, Comparable<GetArrayResponse> {
|
||||
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("GetArrayResponse");
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
package com.jetbrains.python.console.protocol;
|
||||
|
||||
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class KeyboardInterruptException extends org.apache.thrift.TException implements org.apache.thrift.TBase<KeyboardInterruptException, KeyboardInterruptException._Fields>, java.io.Serializable, Cloneable, Comparable<KeyboardInterruptException> {
|
||||
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("KeyboardInterruptException");
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
package com.jetbrains.python.console.protocol;
|
||||
|
||||
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class PythonConsoleFrontendService {
|
||||
|
||||
public interface Iface {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
package com.jetbrains.python.console.protocol;
|
||||
|
||||
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class PythonUnhandledException extends org.apache.thrift.TException implements org.apache.thrift.TBase<PythonUnhandledException, PythonUnhandledException._Fields>, java.io.Serializable, Cloneable, Comparable<PythonUnhandledException> {
|
||||
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("PythonUnhandledException");
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
package com.jetbrains.python.console.protocol;
|
||||
|
||||
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class RowHeader implements org.apache.thrift.TBase<RowHeader, RowHeader._Fields>, java.io.Serializable, Cloneable, Comparable<RowHeader> {
|
||||
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RowHeader");
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
package com.jetbrains.python.console.protocol;
|
||||
|
||||
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-03-16")
|
||||
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2020-06-03")
|
||||
public class UnsupportedArrayTypeException extends org.apache.thrift.TException implements org.apache.thrift.TBase<UnsupportedArrayTypeException, UnsupportedArrayTypeException._Fields>, java.io.Serializable, Cloneable, Comparable<UnsupportedArrayTypeException> {
|
||||
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("UnsupportedArrayTypeException");
|
||||
|
||||
|
||||
@@ -123,20 +123,28 @@ class BaseInterpreterInterface(BaseCodeExecutor):
|
||||
return False
|
||||
|
||||
def execLine(self, line):
|
||||
if not self.banner_shown:
|
||||
line = self.build_banner() + line
|
||||
self.banner_shown = True
|
||||
return self.do_exec_code(line, True)
|
||||
try:
|
||||
if not self.banner_shown:
|
||||
line = self.build_banner() + line
|
||||
self.banner_shown = True
|
||||
return self.do_exec_code(line, True)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
raise PythonUnhandledException(traceback.format_exc())
|
||||
|
||||
def execMultipleLines(self, lines):
|
||||
if not self.banner_shown:
|
||||
lines = self.build_banner() + lines
|
||||
self.banner_shown = True
|
||||
if IS_JYTHON:
|
||||
for line in lines.split('\n'):
|
||||
self.do_exec_code(line, True)
|
||||
else:
|
||||
return self.do_exec_code(lines, False)
|
||||
try:
|
||||
if not self.banner_shown:
|
||||
lines = self.build_banner() + lines
|
||||
self.banner_shown = True
|
||||
if IS_JYTHON:
|
||||
for line in lines.split('\n'):
|
||||
self.do_exec_code(line, True)
|
||||
else:
|
||||
return self.do_exec_code(lines, False)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
raise PythonUnhandledException(traceback.format_exc())
|
||||
|
||||
def interrupt(self):
|
||||
self.buffer = None # Also clear the buffer when it's interrupted.
|
||||
@@ -212,33 +220,48 @@ class BaseInterpreterInterface(BaseCodeExecutor):
|
||||
return True
|
||||
|
||||
def getFrame(self):
|
||||
hidden_ns = self.get_ipython_hidden_vars_dict()
|
||||
return pydevd_thrift.frame_vars_to_struct(self.get_namespace(), hidden_ns)
|
||||
try:
|
||||
hidden_ns = self.get_ipython_hidden_vars_dict()
|
||||
return pydevd_thrift.frame_vars_to_struct(self.get_namespace(), hidden_ns)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
raise PythonUnhandledException(traceback.format_exc())
|
||||
|
||||
def getVariable(self, attributes):
|
||||
debug_values = []
|
||||
val_dict = pydevd_vars.resolve_compound_var_object_fields(self.get_namespace(), attributes)
|
||||
if val_dict is None:
|
||||
val_dict = {}
|
||||
try:
|
||||
debug_values = []
|
||||
val_dict = pydevd_vars.resolve_compound_var_object_fields(self.get_namespace(), attributes)
|
||||
if val_dict is None:
|
||||
val_dict = {}
|
||||
|
||||
keys = val_dict.keys()
|
||||
for k in keys:
|
||||
val = val_dict[k]
|
||||
evaluate_full_value = pydevd_thrift.should_evaluate_full_value(val)
|
||||
debug_values.append(pydevd_thrift.var_to_struct(val, k, evaluate_full_value=evaluate_full_value))
|
||||
keys = val_dict.keys()
|
||||
for k in keys:
|
||||
val = val_dict[k]
|
||||
evaluate_full_value = pydevd_thrift.should_evaluate_full_value(val)
|
||||
debug_values.append(pydevd_thrift.var_to_struct(val, k, evaluate_full_value=evaluate_full_value))
|
||||
|
||||
return debug_values
|
||||
return debug_values
|
||||
except:
|
||||
traceback.print_exc()
|
||||
raise PythonUnhandledException(traceback.format_exc())
|
||||
|
||||
def getArray(self, attr, roffset, coffset, rows, cols, format):
|
||||
name = attr.split("\t")[-1]
|
||||
array = pydevd_vars.eval_in_context(name, self.get_namespace(), self.get_namespace())
|
||||
return pydevd_thrift.table_like_struct_to_thrift_struct(array, name, roffset, coffset, rows, cols, format)
|
||||
try:
|
||||
name = attr.split("\t")[-1]
|
||||
array = pydevd_vars.eval_in_context(name, self.get_namespace(), self.get_namespace())
|
||||
return pydevd_thrift.table_like_struct_to_thrift_struct(array, name, roffset, coffset, rows, cols, format)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
raise PythonUnhandledException(traceback.format_exc())
|
||||
|
||||
def evaluate(self, expression, do_trunc):
|
||||
# returns `DebugValue` of evaluated expression
|
||||
|
||||
result = pydevd_vars.eval_in_context(expression, self.get_namespace(), self.get_namespace())
|
||||
return [pydevd_thrift.var_to_struct(result, expression, do_trim=do_trunc)]
|
||||
try:
|
||||
result = pydevd_vars.eval_in_context(expression, self.get_namespace(), self.get_namespace())
|
||||
return [pydevd_thrift.var_to_struct(result, expression, do_trim=do_trunc)]
|
||||
except:
|
||||
traceback.print_exc()
|
||||
raise PythonUnhandledException(traceback.format_exc())
|
||||
|
||||
def do_get_completions(self, text, act_tok):
|
||||
"""Retrieves completion options.
|
||||
@@ -283,35 +306,43 @@ class BaseInterpreterInterface(BaseCodeExecutor):
|
||||
(i.e.: obj\tattr1\tattr2NEXT_VALUE_SEPARATORobj2\attr1\tattr2)
|
||||
:return:
|
||||
"""
|
||||
frame_variables = self.get_namespace()
|
||||
var_objects = []
|
||||
# vars = scope_attrs.split(NEXT_VALUE_SEPARATOR)
|
||||
vars = scope_attrs
|
||||
for var_attrs in vars:
|
||||
if '\t' in var_attrs:
|
||||
name, attrs = var_attrs.split('\t', 1)
|
||||
try:
|
||||
frame_variables = self.get_namespace()
|
||||
var_objects = []
|
||||
# vars = scope_attrs.split(NEXT_VALUE_SEPARATOR)
|
||||
vars = scope_attrs
|
||||
for var_attrs in vars:
|
||||
if '\t' in var_attrs:
|
||||
name, attrs = var_attrs.split('\t', 1)
|
||||
|
||||
else:
|
||||
name = var_attrs
|
||||
attrs = None
|
||||
if name in frame_variables.keys():
|
||||
var_object = pydevd_vars.resolve_var_object(frame_variables[name], attrs)
|
||||
var_objects.append((var_object, name))
|
||||
else:
|
||||
var_object = pydevd_vars.eval_in_context(name, frame_variables, frame_variables)
|
||||
var_objects.append((var_object, name))
|
||||
else:
|
||||
name = var_attrs
|
||||
attrs = None
|
||||
if name in frame_variables.keys():
|
||||
var_object = pydevd_vars.resolve_var_object(frame_variables[name], attrs)
|
||||
var_objects.append((var_object, name))
|
||||
else:
|
||||
var_object = pydevd_vars.eval_in_context(name, frame_variables, frame_variables)
|
||||
var_objects.append((var_object, name))
|
||||
|
||||
from _pydev_bundle.pydev_console_commands import ThriftGetValueAsyncThreadConsole
|
||||
t = ThriftGetValueAsyncThreadConsole(self.get_server(), seq, var_objects)
|
||||
t.start()
|
||||
from _pydev_bundle.pydev_console_commands import ThriftGetValueAsyncThreadConsole
|
||||
t = ThriftGetValueAsyncThreadConsole(self.get_server(), seq, var_objects)
|
||||
t.start()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
raise PythonUnhandledException(traceback.format_exc())
|
||||
|
||||
def changeVariable(self, attr, value):
|
||||
def do_change_variable():
|
||||
Exec('%s=%s' % (attr, value), self.get_namespace(), self.get_namespace())
|
||||
try:
|
||||
def do_change_variable():
|
||||
Exec('%s=%s' % (attr, value), self.get_namespace(), self.get_namespace())
|
||||
|
||||
# Important: it has to be really enabled in the main thread, so, schedule
|
||||
# it to run in the main thread.
|
||||
self.exec_queue.put(do_change_variable)
|
||||
# Important: it has to be really enabled in the main thread, so, schedule
|
||||
# it to run in the main thread.
|
||||
self.exec_queue.put(do_change_variable)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
raise PythonUnhandledException(traceback.format_exc())
|
||||
|
||||
def _findFrame(self, thread_id, frame_id):
|
||||
'''
|
||||
@@ -333,65 +364,67 @@ class BaseInterpreterInterface(BaseCodeExecutor):
|
||||
Used to show console with variables connection.
|
||||
Mainly, monkey-patches things in the debugger structure so that the debugger protocol works.
|
||||
'''
|
||||
try:
|
||||
if debugger_options is None:
|
||||
debugger_options = {}
|
||||
|
||||
if debugger_options is None:
|
||||
debugger_options = {}
|
||||
|
||||
for (env_name, value) in dict_iter_items(extra_envs):
|
||||
existing_value = os.environ.get(env_name, None)
|
||||
if existing_value:
|
||||
os.environ[env_name] = "%s%c%s" % (existing_value, os.path.pathsep, value)
|
||||
else:
|
||||
os.environ[env_name] = value
|
||||
if env_name == "PYTHONPATH":
|
||||
sys.path.append(value)
|
||||
|
||||
def do_connect_to_debugger():
|
||||
try:
|
||||
# Try to import the packages needed to attach the debugger
|
||||
import pydevd
|
||||
from _pydev_imps._pydev_saved_modules import threading
|
||||
|
||||
except:
|
||||
# This happens on Jython embedded in host eclipse
|
||||
traceback.print_exc()
|
||||
sys.stderr.write('pydevd is not available, cannot connect\n', )
|
||||
|
||||
from _pydevd_bundle.pydevd_constants import set_thread_id
|
||||
from _pydev_bundle import pydev_localhost
|
||||
set_thread_id(threading.currentThread(), "console_main")
|
||||
|
||||
self.orig_find_frame = pydevd_vars.find_frame
|
||||
pydevd_vars.find_frame = self._findFrame
|
||||
|
||||
self.debugger = pydevd.PyDB()
|
||||
try:
|
||||
pydevd.apply_debugger_options(debugger_options)
|
||||
if debugger_host is None or pydev_localhost.is_localhost(debugger_host):
|
||||
host = pydev_localhost.get_localhost()
|
||||
for (env_name, value) in dict_iter_items(extra_envs):
|
||||
existing_value = os.environ.get(env_name, None)
|
||||
if existing_value:
|
||||
os.environ[env_name] = "%s%c%s" % (existing_value, os.path.pathsep, value)
|
||||
else:
|
||||
host = debugger_host
|
||||
self.debugger.connect(host, debuggerPort)
|
||||
self.debugger.prepare_to_run()
|
||||
self.debugger.disable_tracing()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
sys.stderr.write('Failed to connect to target debugger.\n')
|
||||
os.environ[env_name] = value
|
||||
if env_name == "PYTHONPATH":
|
||||
sys.path.append(value)
|
||||
|
||||
# Register to process commands when idle
|
||||
self.debugrunning = False
|
||||
try:
|
||||
import pydevconsole
|
||||
pydevconsole.set_debug_hook(self.debugger.process_internal_commands)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
sys.stderr.write('Version of Python does not support debuggable Interactive Console.\n')
|
||||
def do_connect_to_debugger():
|
||||
try:
|
||||
# Try to import the packages needed to attach the debugger
|
||||
import pydevd
|
||||
from _pydev_imps._pydev_saved_modules import threading
|
||||
|
||||
# Important: it has to be really enabled in the main thread, so, schedule
|
||||
# it to run in the main thread.
|
||||
self.exec_queue.put(do_connect_to_debugger)
|
||||
except:
|
||||
# This happens on Jython embedded in host eclipse
|
||||
traceback.print_exc()
|
||||
sys.stderr.write('pydevd is not available, cannot connect\n', )
|
||||
|
||||
return ('connect complete',)
|
||||
from _pydevd_bundle.pydevd_constants import set_thread_id
|
||||
from _pydev_bundle import pydev_localhost
|
||||
set_thread_id(threading.currentThread(), "console_main")
|
||||
|
||||
self.orig_find_frame = pydevd_vars.find_frame
|
||||
pydevd_vars.find_frame = self._findFrame
|
||||
|
||||
self.debugger = pydevd.PyDB()
|
||||
try:
|
||||
pydevd.apply_debugger_options(debugger_options)
|
||||
if debugger_host is None or pydev_localhost.is_localhost(debugger_host):
|
||||
host = pydev_localhost.get_localhost()
|
||||
else:
|
||||
host = debugger_host
|
||||
self.debugger.connect(host, debuggerPort)
|
||||
self.debugger.prepare_to_run()
|
||||
self.debugger.disable_tracing()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
sys.stderr.write('Failed to connect to target debugger.\n')
|
||||
|
||||
# Register to process commands when idle
|
||||
self.debugrunning = False
|
||||
try:
|
||||
import pydevconsole
|
||||
pydevconsole.set_debug_hook(self.debugger.process_internal_commands)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
sys.stderr.write('Version of Python does not support debuggable Interactive Console.\n')
|
||||
|
||||
# Important: it has to be really enabled in the main thread, so, schedule
|
||||
# it to run in the main thread.
|
||||
self.exec_queue.put(do_connect_to_debugger)
|
||||
return ('connect complete',)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
raise PythonUnhandledException(traceback.format_exc())
|
||||
|
||||
def handshake(self):
|
||||
if self.connect_status_queue is not None:
|
||||
|
||||
@@ -132,13 +132,13 @@ service PythonConsoleBackendService {
|
||||
* Returns `true` if Python console script needs more code to evaluate it.
|
||||
* Returns `false` if the code is scheduled for evaluation.
|
||||
*/
|
||||
bool execLine(1: string line),
|
||||
bool execLine(1: string line) throws (1: PythonUnhandledException unhandledException),
|
||||
|
||||
/**
|
||||
* Returns `true` if Python console script needs more code to evaluate it.
|
||||
* Returns `false` if the code is scheduled for evaluation.
|
||||
*/
|
||||
bool execMultipleLines(1: string lines),
|
||||
bool execMultipleLines(1: string lines) throws (1: PythonUnhandledException unhandledException),
|
||||
|
||||
GetCompletionsResponse getCompletions(1: string text, 2: string actTok) throws (1: PythonUnhandledException unhandledException),
|
||||
|
||||
@@ -150,19 +150,20 @@ service PythonConsoleBackendService {
|
||||
/**
|
||||
* Return Frame
|
||||
*/
|
||||
GetFrameResponse getFrame(),
|
||||
GetFrameResponse getFrame() throws (1: PythonUnhandledException unhandledException),
|
||||
|
||||
/**
|
||||
* Parameter is a full path in a variables tree from the top-level parent to the debug value.
|
||||
**/
|
||||
DebugValues getVariable(1: string variable),
|
||||
DebugValues getVariable(1: string variable) throws (1: PythonUnhandledException unhandledException),
|
||||
|
||||
/**
|
||||
* Changes the variable value asynchronously.
|
||||
*/
|
||||
void changeVariable(1: string evaluationExpression, 2: string value),
|
||||
void changeVariable(1: string evaluationExpression, 2: string value) throws (1: PythonUnhandledException unhandledException),
|
||||
|
||||
void connectToDebugger(1: i32 localPort, 2: string host, 3: map<string, bool> opts, 4: map<string, string> extraEnvs),
|
||||
void connectToDebugger(1: i32 localPort, 2: string host, 3: map<string, bool> opts, 4: map<string, string> extraEnvs)
|
||||
throws (1: PythonUnhandledException unhandledException),
|
||||
|
||||
void interrupt(),
|
||||
|
||||
@@ -176,15 +177,17 @@ service PythonConsoleBackendService {
|
||||
*/
|
||||
oneway void close(),
|
||||
|
||||
DebugValues evaluate(1: string expression, 2: bool doTrunc),
|
||||
DebugValues evaluate(1: string expression, 2: bool doTrunc) throws (1: PythonUnhandledException unhandledException),
|
||||
|
||||
GetArrayResponse getArray(1: string vars, 2: i32 rowOffset, 3: i32 colOffset, 4: i32 rows, 5: i32 cols, 6: string format)
|
||||
throws (1: UnsupportedArrayTypeException unsupported, 2: ExceedingArrayDimensionsException exceedingDimensions),
|
||||
throws (1: UnsupportedArrayTypeException unsupported, 2: ExceedingArrayDimensionsException exceedingDimensions,
|
||||
3: PythonUnhandledException unhandledException),
|
||||
|
||||
|
||||
/**
|
||||
* The result is returned asyncronously with `PythonConsoleFrontendService.returnFullValue`.
|
||||
*/
|
||||
void loadFullValue(1: LoadFullValueRequestSeq seq, 2: list<string> variables),
|
||||
void loadFullValue(1: LoadFullValueRequestSeq seq, 2: list<string> variables) throws (1: PythonUnhandledException unhandledException),
|
||||
}
|
||||
|
||||
exception KeyboardInterruptException {
|
||||
|
||||
@@ -707,10 +707,17 @@ configurable.choose.path.to.the.package.requirements.file=Choose path to the pac
|
||||
configurable.choose.working.directory=Please choose working directory:
|
||||
configurable.select.working.directory=Select Working Directory
|
||||
|
||||
console.waiting.for.repl.response.with.timeout=Waiting for REPL response with {0}s timeout
|
||||
console.waiting.for.repl.response=Waiting for REPL Response
|
||||
console.repl.communication=REPL Communication
|
||||
console.getting.description=Getting Description
|
||||
console.waiting.execution.result=Waiting for Execution Result
|
||||
console.getting.from.runtime=Getting {0} from Python Runtime
|
||||
console.getting.completion=Completion
|
||||
console.getting.documentation=Documentation
|
||||
console.getting.frame.variables=Frame Variables
|
||||
console.getting.variable.value=Variable Value
|
||||
console.getting.array=Array
|
||||
console.evaluating.expression.in.console=Evaluating Expression in Console
|
||||
console.connecting.to.debugger=Connecting To Debugger
|
||||
console.changing.variable=Changing Variable
|
||||
console.interrupting.execution=Interrupting Execution
|
||||
console.close.console.communication=Close Console Communication
|
||||
console.executing.code.in.console=Executing Code in Console...
|
||||
console.new.console.description=Creates new python console
|
||||
@@ -720,6 +727,7 @@ console.cannot.connect.to.debugger=Can't connect to debugger
|
||||
console.attach.debugger.description=Enables tracing of code executed in console
|
||||
console.attach.debugger=Attach Debugger
|
||||
console.restarting.console=Restarting Console
|
||||
console.stopping.console=Stopping Console
|
||||
console.command.executor=Python Console Command Executor
|
||||
connecting.to.console.progress=Connecting to console...
|
||||
connecting.to.console.title=Connecting to Console
|
||||
|
||||
@@ -11,11 +11,11 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.ThrowableComputable;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.concurrency.FutureResult;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.intellij.xdebugger.frame.XCompositeNode;
|
||||
@@ -39,11 +39,10 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.jetbrains.python.console.PydevConsoleCommunicationUtil.*;
|
||||
|
||||
@@ -63,21 +62,16 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
/**
|
||||
* Response that should be sent back to the shell.
|
||||
*/
|
||||
protected volatile InterpreterResponse nextResponse;
|
||||
/**
|
||||
* Helper to keep on busy loop.
|
||||
*/
|
||||
private final Object lock2 = new Object();
|
||||
/**
|
||||
* Keeps a flag indicating that we were able to communicate successfully with the shell at least once
|
||||
* (if we haven't we may retry more than once the first time, as jython can take a while to initialize
|
||||
* the communication)
|
||||
*/
|
||||
private volatile boolean firstCommWorked = false;
|
||||
@Nullable protected volatile InterpreterResponse nextResponse;
|
||||
|
||||
private boolean myExecuting;
|
||||
private PythonDebugConsoleCommunication myDebugCommunication;
|
||||
private boolean myNeedsMore = false;
|
||||
/**
|
||||
* UI sends a lot of repeated requests for the same expression
|
||||
* Store result of the previous request to avoid sending the same command several times
|
||||
*/
|
||||
@Nullable private Pair<String, String> myPrevNameToDescription = null;
|
||||
|
||||
private int myFullValueSeq = 0;
|
||||
private final Map<Integer, List<PyFrameAccessor.PyAsyncValue<String>>> myCallbackHashMap = new ConcurrentHashMap<>();
|
||||
@@ -140,7 +134,7 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
PyDebugValueExecutionService.getInstance(myProject).sessionStopped(this);
|
||||
myCallbackHashMap.clear();
|
||||
|
||||
new Task.Backgroundable(myProject, PyBundle.message("console.close.console.communication"), true) {
|
||||
new Task.Backgroundable(myProject, PyBundle.message("console.close.console.communication"), false) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
try {
|
||||
@@ -185,7 +179,7 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
@NotNull
|
||||
protected abstract Future<?> closeCommunication();
|
||||
|
||||
protected abstract boolean isCommunicationClosed();
|
||||
public abstract boolean isCommunicationClosed();
|
||||
|
||||
/*
|
||||
* Variables that control when we're expecting to give some input to the server or when we're
|
||||
@@ -263,7 +257,7 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
* @param command
|
||||
* @return a Pair with (null, more) or (error, false)
|
||||
*/
|
||||
protected Pair<String, Boolean> exec(final ConsoleCodeFragment command) {
|
||||
protected Pair<String, Boolean> exec(final ConsoleCodeFragment command) throws PythonUnhandledException {
|
||||
setExecuting(true);
|
||||
|
||||
boolean more;
|
||||
@@ -275,7 +269,12 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
more = getPythonConsoleBackendClient().execMultipleLines(command.getText());
|
||||
}
|
||||
}
|
||||
catch (PythonUnhandledException e) {
|
||||
setExecuting(false);
|
||||
throw e;
|
||||
}
|
||||
catch (TException e) {
|
||||
setExecuting(false);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -286,6 +285,10 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
return Pair.create(null, more);
|
||||
}
|
||||
|
||||
private static String createRuntimeMessage(String taskName) {
|
||||
return PyBundle.message("console.getting.from.runtime", taskName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return completions from the client
|
||||
*/
|
||||
@@ -295,6 +298,8 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
if (waitingForInput || isExecuting()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
indicator.setText(createRuntimeMessage(PyBundle.message("console.getting.completion")));
|
||||
return ApplicationUtil.runWithCheckCanceled(
|
||||
() ->
|
||||
{
|
||||
@@ -312,7 +317,7 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
return Collections.emptyList();
|
||||
}
|
||||
},
|
||||
ProgressManager.getInstance().getProgressIndicator());
|
||||
indicator);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -321,6 +326,48 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
return new PydevCompletionVariant(option.name, option.documentation, args, option.type.getValue());
|
||||
}
|
||||
|
||||
private <T> void executeBackgroundTaskSuppressException(Callable<T> task,
|
||||
String userVisibleMessage,
|
||||
String errorLogMessage) {
|
||||
try {
|
||||
executeBackgroundTask(task, false, userVisibleMessage, errorLogMessage);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T> T executeBackgroundTask(Callable<T> task, boolean waitForResult, String userVisibleMessage, String errorLogMessage)
|
||||
throws PyDebuggerException {
|
||||
final FutureResult<T> future = new FutureResult<>();
|
||||
new Task.Backgroundable(myProject, userVisibleMessage, false) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
try {
|
||||
T result = task.call();
|
||||
future.set(result);
|
||||
}
|
||||
catch (PythonUnhandledException e) {
|
||||
LOG.error(errorLogMessage + e.traceback);
|
||||
future.set(null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
future.setException(e);
|
||||
}
|
||||
}
|
||||
}.queue();
|
||||
if (waitForResult) {
|
||||
try {
|
||||
return future.get();
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e) {
|
||||
throw new PyDebuggerException(errorLogMessage, e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the description of the given attribute in the shell
|
||||
*/
|
||||
@@ -332,15 +379,27 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
if (waitingForInput) {
|
||||
return "Unable to get description: waiting for input.";
|
||||
}
|
||||
|
||||
ThrowableComputable<String, Exception> doGetDesc = () -> getPythonConsoleBackendClient().getDescription(text);
|
||||
if (ApplicationManager.getApplication().isDispatchThread()) {
|
||||
return ProgressManager.getInstance().runProcessWithProgressSynchronously(doGetDesc, PyBundle.message("console.getting.description"), true, myProject);
|
||||
if (myPrevNameToDescription != null) {
|
||||
if (myPrevNameToDescription.first.equals(text)) return myPrevNameToDescription.second;
|
||||
}
|
||||
else {
|
||||
// note that the thread would still wait for the response after the timeout occurs
|
||||
return ApplicationManager.getApplication().executeOnPooledThread(() -> doGetDesc.compute()).get(5, TimeUnit.SECONDS);
|
||||
// add temporary value to avoid repeated requests for the same expression
|
||||
myPrevNameToDescription = Pair.create(text, "");
|
||||
}
|
||||
if (ApplicationManager.getApplication().isDispatchThread()) {
|
||||
throw new PyDebuggerException("Documentation in Python Console shouldn't be called from Dispatch Thread!");
|
||||
}
|
||||
|
||||
return executeBackgroundTask(
|
||||
() ->
|
||||
{
|
||||
final String resultDescription = getPythonConsoleBackendClient().getDescription(text);
|
||||
myPrevNameToDescription = Pair.create(text, resultDescription);
|
||||
return resultDescription;
|
||||
},
|
||||
true,
|
||||
createRuntimeMessage(PyBundle.message("console.getting.documentation")),
|
||||
"Error when Getting Description in Python Console: ");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,108 +421,34 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
}
|
||||
else {
|
||||
//create a thread that'll keep locked until an answer is received from the server.
|
||||
new Task.Backgroundable(myProject, PyBundle.message("console.repl.communication"), true) {
|
||||
|
||||
new Task.Backgroundable(myProject, PyBundle.message("console.waiting.execution.result"), false) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
boolean needInput = false;
|
||||
try {
|
||||
|
||||
Pair<String, Boolean> executed = null;
|
||||
|
||||
//the 1st time we'll do a connection attempt, we can try to connect n times (until the 1st time the connection
|
||||
//is accepted) -- that's mostly because the server may take a while to get started.
|
||||
int commAttempts = 0;
|
||||
while (true) {
|
||||
if (indicator.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
executed = exec(command);
|
||||
|
||||
//executed.o1 is not null only if we had an error
|
||||
|
||||
String refusedConnPattern = "Failed to read servers response";
|
||||
// Was "refused", but it didn't
|
||||
// work on non English system
|
||||
// (in Spanish localized systems
|
||||
// it is "rechazada")
|
||||
// This string always works,
|
||||
// because it is hard-coded in
|
||||
// the XML-RPC library)
|
||||
if (executed.first != null && executed.first.contains(refusedConnPattern)) {
|
||||
if (firstCommWorked) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (commAttempts < MAX_ATTEMPTS) {
|
||||
commAttempts += 1;
|
||||
Thread.sleep(250);
|
||||
executed = Pair.create("", executed.second);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
//unreachable code!! -- commented because eclipse will complain about it
|
||||
//throw new RuntimeException("Can never get here!");
|
||||
}
|
||||
|
||||
firstCommWorked = true;
|
||||
|
||||
boolean more = executed.second;
|
||||
|
||||
nextResponse = new InterpreterResponse(more, needInput);
|
||||
if (indicator.isCanceled()) return;
|
||||
Pair<String, Boolean> executed = exec(command);
|
||||
nextResponse = new InterpreterResponse(executed.second, false);
|
||||
}
|
||||
catch (ProcessCanceledException e) {
|
||||
//ignore
|
||||
}
|
||||
catch (PythonUnhandledException e) {
|
||||
LOG.error("Error in execInterpreter():" + e.traceback);
|
||||
nextResponse = new InterpreterResponse(false, false);
|
||||
}
|
||||
catch (Exception e) {
|
||||
nextResponse = new InterpreterResponse(false, needInput);
|
||||
nextResponse = new InterpreterResponse(false, false);
|
||||
}
|
||||
finally {
|
||||
InterpreterResponse response = nextResponse;
|
||||
if (response != null && response.more) {
|
||||
myNeedsMore = true;
|
||||
}
|
||||
notifyCommandExecuted(true);
|
||||
onResponseReceived.fun(response);
|
||||
}
|
||||
}
|
||||
}.queue();
|
||||
|
||||
ProgressManager.getInstance().run(new Task.Backgroundable(myProject, PyBundle.message("console.waiting.for.repl.response")) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
progressIndicator.setText(PyBundle.message("console.waiting.for.repl.response.with.timeout", (int)(TIMEOUT / 10e8)));
|
||||
progressIndicator.setIndeterminate(false);
|
||||
final long startTime = System.nanoTime();
|
||||
while (nextResponse == null) {
|
||||
if (progressIndicator.isCanceled()) {
|
||||
LOG.debug("Canceled");
|
||||
nextResponse = new InterpreterResponse(false, false);
|
||||
}
|
||||
|
||||
final long time = System.nanoTime() - startTime;
|
||||
progressIndicator.setFraction(((double)time) / TIMEOUT);
|
||||
if (time > TIMEOUT) {
|
||||
LOG.debug("Timeout exceeded");
|
||||
nextResponse = new InterpreterResponse(false, false);
|
||||
}
|
||||
synchronized (lock2) {
|
||||
try {
|
||||
lock2.wait(20);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextResponse.more) {
|
||||
myNeedsMore = true;
|
||||
notifyCommandExecuted(true);
|
||||
}
|
||||
onResponseReceived.fun(nextResponse);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,12 +460,14 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
keyboardInterruption = true;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
getPythonConsoleBackendClient().interrupt();
|
||||
}
|
||||
catch (CommunicationClosedException | PyConsoleProcessFinishedException | TException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
executeBackgroundTaskSuppressException(
|
||||
() ->
|
||||
{
|
||||
getPythonConsoleBackendClient().interrupt();
|
||||
return null;
|
||||
},
|
||||
PyBundle.message("console.interrupting.execution"),
|
||||
"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -496,13 +483,15 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
@Override
|
||||
public PyDebugValue evaluate(String expression, boolean execute, boolean doTrunc) throws PyDebuggerException {
|
||||
if (!isCommunicationClosed()) {
|
||||
try {
|
||||
List<DebugValue> debugValues = getPythonConsoleBackendClient().evaluate(expression, doTrunc);
|
||||
return createPyDebugValue(debugValues.iterator().next(), this);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new PyDebuggerException("Evaluate in console failed", e);
|
||||
}
|
||||
return executeBackgroundTask(
|
||||
() -> {
|
||||
List<DebugValue> debugValues = getPythonConsoleBackendClient().evaluate(expression, doTrunc);
|
||||
return createPyDebugValue(debugValues.iterator().next(), this);
|
||||
},
|
||||
true,
|
||||
PyBundle.message("console.evaluating.expression.in.console"),
|
||||
"Error in evaluate():"
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -511,13 +500,15 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
@Override
|
||||
public XValueChildrenList loadFrame() throws PyDebuggerException {
|
||||
if (!isCommunicationClosed()) {
|
||||
try {
|
||||
List<DebugValue> frame = getPythonConsoleBackendClient().getFrame();
|
||||
return parseVars(frame, null, this);
|
||||
}
|
||||
catch (CommunicationClosedException | PyConsoleProcessFinishedException | TException e) {
|
||||
throw new PyDebuggerException("Get frame from console failed", e);
|
||||
}
|
||||
return executeBackgroundTask(
|
||||
() -> {
|
||||
List<DebugValue> frame = getPythonConsoleBackendClient().getFrame();
|
||||
return parseVars(frame, null, this);
|
||||
},
|
||||
true,
|
||||
createRuntimeMessage(PyBundle.message("console.getting.frame.variables")),
|
||||
"Error in loadFrame():"
|
||||
);
|
||||
}
|
||||
return new XValueChildrenList();
|
||||
}
|
||||
@@ -562,18 +553,17 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
@Override
|
||||
public XValueChildrenList loadVariable(PyDebugValue var) throws PyDebuggerException {
|
||||
if (!isCommunicationClosed()) {
|
||||
try {
|
||||
final String name = var.getOffset() == 0 ? GetVariableCommand.composeName(var)
|
||||
: var.getOffset() + "\t" + GetVariableCommand.composeName(var);
|
||||
List<DebugValue> ret = getPythonConsoleBackendClient().getVariable(name);
|
||||
return parseVars(ret, var, this);
|
||||
}
|
||||
catch (CommunicationClosedException | PyConsoleProcessFinishedException e) {
|
||||
throw new PyDebuggerException(e.getLocalizedMessage(), e);
|
||||
}
|
||||
catch (TException e) {
|
||||
throw new PyDebuggerException("Get variable from console failed", e);
|
||||
}
|
||||
return executeBackgroundTask(
|
||||
() -> {
|
||||
final String name = var.getOffset() == 0 ? GetVariableCommand.composeName(var)
|
||||
: var.getOffset() + "\t" + GetVariableCommand.composeName(var);
|
||||
List<DebugValue> ret = getPythonConsoleBackendClient().getVariable(name);
|
||||
return parseVars(ret, var, this);
|
||||
},
|
||||
true,
|
||||
createRuntimeMessage(PyBundle.message("console.getting.variable.value")),
|
||||
"Error in loadVariable():"
|
||||
);
|
||||
}
|
||||
return new XValueChildrenList();
|
||||
}
|
||||
@@ -595,16 +585,18 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeVariable(PyDebugValue variable, String value) throws PyDebuggerException {
|
||||
public void changeVariable(PyDebugValue variable, String value) {
|
||||
if (!isCommunicationClosed()) {
|
||||
try {
|
||||
// NOTE: The actual change is being scheduled in the exec_queue in main thread
|
||||
// This method is async now
|
||||
getPythonConsoleBackendClient().changeVariable(variable.getEvaluationExpression(), value);
|
||||
}
|
||||
catch (CommunicationClosedException | PyConsoleProcessFinishedException | TException e) {
|
||||
throw new PyDebuggerException("Get change variable", e);
|
||||
}
|
||||
executeBackgroundTaskSuppressException(
|
||||
() -> {
|
||||
// NOTE: The actual change is being scheduled in the exec_queue in main thread
|
||||
// This method is async now
|
||||
getPythonConsoleBackendClient().changeVariable(variable.getEvaluationExpression(), value);
|
||||
return null;
|
||||
},
|
||||
PyBundle.message("console.changing.variable"),
|
||||
"Error in changeVariable():"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,19 +610,15 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
public ArrayChunk getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format)
|
||||
throws PyDebuggerException {
|
||||
if (!isCommunicationClosed()) {
|
||||
try {
|
||||
GetArrayResponse ret = getPythonConsoleBackendClient().getArray(var.getName(), rowOffset, colOffset, rows, cols, format);
|
||||
return createArrayChunk(ret, this);
|
||||
}
|
||||
catch (UnsupportedArrayTypeException e) {
|
||||
throw new IllegalArgumentException(var.getType() + " is not supported", e);
|
||||
}
|
||||
catch (ExceedingArrayDimensionsException e) {
|
||||
throw new IllegalArgumentException(var.getName() + " has more than two dimensions", e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new PyDebuggerException("Evaluate in console failed", e);
|
||||
}
|
||||
executeBackgroundTask(
|
||||
() -> {
|
||||
GetArrayResponse ret = getPythonConsoleBackendClient().getArray(var.getName(), rowOffset, colOffset, rows, cols, format);
|
||||
return createArrayChunk(ret, this);
|
||||
},
|
||||
true,
|
||||
createRuntimeMessage(PyBundle.message("console.getting.array")),
|
||||
"Error in getArrayItems():"
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -660,16 +648,18 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
if (waitingForInput) {
|
||||
throw new Exception("Can't connect debugger now, waiting for input");
|
||||
}
|
||||
try {
|
||||
// though `connectToDebugger` returns "connect complete" string, let us just ignore it
|
||||
getPythonConsoleBackendClient().connectToDebugger(localPort, debuggerHost, dbgOpts, extraEnvs);
|
||||
}
|
||||
catch (CommunicationClosedException | PyConsoleProcessFinishedException | TException e) {
|
||||
throw new PyDebuggerException("pydevconsole failed to execute connectToDebugger", e);
|
||||
}
|
||||
executeBackgroundTask(
|
||||
() -> {
|
||||
// though `connectToDebugger` returns "connect complete" string, let us just ignore it
|
||||
getPythonConsoleBackendClient().connectToDebugger(localPort, debuggerHost, dbgOpts, extraEnvs);
|
||||
return null;
|
||||
},
|
||||
true,
|
||||
PyBundle.message("console.connecting.to.debugger"),
|
||||
"Error in connectToDebugger():"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void notifyCommandExecuted(boolean more) {
|
||||
super.notifyCommandExecuted(more);
|
||||
@@ -733,9 +723,7 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
|
||||
public void returnFullValue(int requestSeq, List<DebugValue> response) {
|
||||
final List<PyAsyncValue<String>> values = myCallbackHashMap.remove(requestSeq);
|
||||
try {
|
||||
List<PyDebugValue> debugValues = response.stream()
|
||||
.map(value -> createPyDebugValue(value, PydevConsoleCommunication.this))
|
||||
.collect(Collectors.toList());
|
||||
List<PyDebugValue> debugValues = ContainerUtil.map(response, value -> createPyDebugValue(value, PydevConsoleCommunication.this));
|
||||
for (int i = 0; i < debugValues.size(); ++i) {
|
||||
PyDebugValue resultValue = debugValues.get(i);
|
||||
values.get(i).getCallback().ok(resultValue.getValue());
|
||||
|
||||
@@ -116,6 +116,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
|
||||
public static final @NonNls String CONSOLE_START_COMMAND = "import sys; print('Python %s on %s' % (sys.version, sys.platform))\n" +
|
||||
"sys.path.extend([" + WORKING_DIR_AND_PYTHON_PATHS + "])\n";
|
||||
public static final @NonNls String STARTED_BY_RUNNER = "startedByRunner";
|
||||
private static final Long WAIT_BEFORE_FORCED_CLOSE_MILLIS = 2000L;
|
||||
private static final Logger LOG = Logger.getInstance(PydevConsoleRunnerImpl.class);
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
public static final @NonNls String PYDEV_PYDEVCONSOLE_PY = "pydev/pydevconsole.py";
|
||||
@@ -674,7 +675,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
stopConsole();
|
||||
stopAndRerunConsole(false, PyBundle.message("console.stopping.console"), null);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -706,19 +707,6 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
|
||||
}
|
||||
}
|
||||
|
||||
private void stopConsole() {
|
||||
if (myPydevConsoleCommunication != null) {
|
||||
try {
|
||||
closeCommunication();
|
||||
// waiting for REPL communication before destroying process handler
|
||||
Thread.sleep(300);
|
||||
}
|
||||
catch (Exception ignored) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected AnAction createSplitLineAction() {
|
||||
|
||||
class ConsoleSplitLineAction extends EditorAction {
|
||||
@@ -824,7 +812,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
|
||||
displayName = name;
|
||||
}
|
||||
}
|
||||
myConsoleRunner.rerun(displayName);
|
||||
myConsoleRunner.stopAndRerunConsole(true, PyBundle.message("console.restarting.console"), displayName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -838,21 +826,23 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
|
||||
return content.getDisplayName();
|
||||
}
|
||||
|
||||
private void rerun(String displayName) {
|
||||
new Task.Backgroundable(myProject, PyBundle.message("console.restarting.console"), true) {
|
||||
private void stopAndRerunConsole(Boolean rerun, @NotNull String message, @Nullable String displayName) {
|
||||
new Task.Backgroundable(myProject, message, true) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
if (myProcessHandler != null) {
|
||||
UIUtil.invokeAndWaitIfNeeded((Runnable)() -> closeCommunication());
|
||||
|
||||
boolean processStopped = myProcessHandler.waitFor(5000L);
|
||||
boolean processStopped = myProcessHandler.waitFor(WAIT_BEFORE_FORCED_CLOSE_MILLIS);
|
||||
if (!processStopped && myProcessHandler.canKillProcess()) {
|
||||
myProcessHandler.killProcess();
|
||||
}
|
||||
myProcessHandler.waitFor();
|
||||
}
|
||||
|
||||
GuiUtils.invokeLaterIfNeeded(() -> myRerunAction.consume(displayName), ModalityState.defaultModalityState());
|
||||
if (rerun) {
|
||||
GuiUtils.invokeLaterIfNeeded(() -> myRerunAction.consume(displayName), ModalityState.defaultModalityState());
|
||||
}
|
||||
}
|
||||
}.queue();
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ public final class PyDataView implements DumbAware {
|
||||
}
|
||||
|
||||
private static boolean isConnected(PydevConsoleCommunication accessor){
|
||||
return accessor.handshake();
|
||||
return !accessor.isCommunicationClosed();
|
||||
}
|
||||
|
||||
public static PyDataView getInstance(@NotNull final Project project) {
|
||||
|
||||
Reference in New Issue
Block a user