mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
473 lines
18 KiB
Python
473 lines
18 KiB
Python
import os
|
|
import sys
|
|
import traceback
|
|
|
|
from _pydev_bundle import _pydev_imports_tipper
|
|
from _pydev_bundle.pydev_code_executor import BaseCodeExecutor
|
|
from _pydev_bundle.pydev_console_types import CodeFragment
|
|
from _pydev_bundle.pydev_imports import Exec
|
|
from _pydev_bundle.pydev_stdin import StdIn, DebugConsoleStdIn
|
|
from _pydevd_bundle import pydevd_thrift
|
|
from _pydevd_bundle import pydevd_vars
|
|
from _pydevd_bundle.pydevd_comm import InternalDataViewerAction
|
|
from _pydevd_bundle.pydevd_constants import IS_JYTHON, dict_iter_items
|
|
from _pydevd_bundle.pydevd_tables import exec_table_command
|
|
from _pydevd_bundle.pydevd_tables import exec_image_table_command
|
|
from _pydevd_bundle.pydevd_user_type_renderers import parse_set_type_renderers_message
|
|
from pydev_console.pydev_protocol import CompletionOption, CompletionOptionType, \
|
|
PythonUnhandledException, PythonTableException
|
|
|
|
try:
|
|
import cStringIO as StringIO # may not always be available @UnusedImport
|
|
except:
|
|
try:
|
|
import StringIO # @Reimport
|
|
except:
|
|
import io as StringIO
|
|
|
|
# translation to Thrift `CompletionOptionType` enumeration
|
|
COMPLETION_OPTION_TYPES = {
|
|
_pydev_imports_tipper.TYPE_IMPORT: CompletionOptionType.IMPORT,
|
|
_pydev_imports_tipper.TYPE_CLASS: CompletionOptionType.CLASS,
|
|
_pydev_imports_tipper.TYPE_FUNCTION: CompletionOptionType.FUNCTION,
|
|
_pydev_imports_tipper.TYPE_ATTR: CompletionOptionType.ATTR,
|
|
_pydev_imports_tipper.TYPE_BUILTIN: CompletionOptionType.BUILTIN,
|
|
_pydev_imports_tipper.TYPE_PARAM: CompletionOptionType.PARAM,
|
|
_pydev_imports_tipper.TYPE_IPYTHON: CompletionOptionType.IPYTHON,
|
|
_pydev_imports_tipper.TYPE_IPYTHON_MAGIC: CompletionOptionType.IPYTHON_MAGIC,
|
|
}
|
|
|
|
|
|
def _to_completion_option(word):
|
|
name, documentation, args, ret_type = word
|
|
completion_option_type = COMPLETION_OPTION_TYPES[ret_type]
|
|
return CompletionOption(name, documentation, args.split(), completion_option_type)
|
|
|
|
|
|
# =======================================================================================================================
|
|
# Null
|
|
# =======================================================================================================================
|
|
class Null:
|
|
"""
|
|
Gotten from: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
return None
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
return self
|
|
|
|
def __getattr__(self, mname):
|
|
return self
|
|
|
|
def __setattr__(self, name, value):
|
|
return self
|
|
|
|
def __delattr__(self, name):
|
|
return self
|
|
|
|
def __repr__(self):
|
|
return "<Null>"
|
|
|
|
def __str__(self):
|
|
return "Null"
|
|
|
|
def __len__(self):
|
|
return 0
|
|
|
|
def __getitem__(self):
|
|
return self
|
|
|
|
def __setitem__(self, *args, **kwargs):
|
|
pass
|
|
|
|
def write(self, *args, **kwargs):
|
|
pass
|
|
|
|
def __nonzero__(self):
|
|
return 0
|
|
|
|
|
|
# =======================================================================================================================
|
|
# BaseInterpreterInterface
|
|
# =======================================================================================================================
|
|
class BaseInterpreterInterface(BaseCodeExecutor):
|
|
def __init__(self, mainThread, connect_status_queue=None, rpc_client=None):
|
|
super(BaseInterpreterInterface, self).__init__()
|
|
|
|
self.mainThread = mainThread
|
|
self.banner_shown = False
|
|
self.connect_status_queue = connect_status_queue
|
|
self.user_type_renderers = {}
|
|
|
|
self.rpc_client = rpc_client
|
|
self._first_command_executed = False
|
|
|
|
def build_banner(self):
|
|
return 'print({0})\n'.format(repr(self.get_greeting_msg()))
|
|
|
|
def create_std_in(self, debugger=None, original_std_in=None):
|
|
if debugger is None:
|
|
return StdIn(self, self.rpc_client, original_stdin=original_std_in)
|
|
else:
|
|
return DebugConsoleStdIn(dbg=debugger, original_stdin=original_std_in)
|
|
|
|
def do_exec_code(self, code, is_single_line):
|
|
try:
|
|
code_fragment = CodeFragment(code, is_single_line)
|
|
more = self.need_more(code_fragment)
|
|
if not more:
|
|
code_fragment = self.buffer
|
|
self.buffer = None
|
|
self.exec_queue.put(code_fragment)
|
|
|
|
return more
|
|
except:
|
|
traceback.print_exc()
|
|
return False
|
|
|
|
def execLine(self, line):
|
|
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):
|
|
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 close(self):
|
|
sys.exit(0)
|
|
|
|
def get_server(self):
|
|
if getattr(self, 'rpc_client', None) is not None:
|
|
return self.rpc_client
|
|
else:
|
|
return None
|
|
|
|
server = property(get_server)
|
|
|
|
def ShowConsole(self):
|
|
server = self.get_server()
|
|
if server is not None:
|
|
server.showConsole()
|
|
|
|
def notify_first_command_executed(self):
|
|
pass
|
|
|
|
def finish_exec(self, more, exception_occurred):
|
|
self.interruptable = False
|
|
|
|
if not self._first_command_executed:
|
|
self.notify_first_command_executed()
|
|
self._first_command_executed = True
|
|
|
|
server = self.get_server()
|
|
|
|
if server is not None:
|
|
return server.notifyFinished(more, exception_occurred)
|
|
else:
|
|
return True
|
|
|
|
def getFrame(self, group_type):
|
|
try:
|
|
hidden_ns = self.get_ipython_hidden_vars_dict()
|
|
return pydevd_thrift.frame_vars_to_struct(self.get_namespace(), group_type, hidden_ns, self.user_type_renderers)
|
|
except:
|
|
traceback.print_exc()
|
|
raise PythonUnhandledException(traceback.format_exc())
|
|
|
|
def getVariable(self, attributes):
|
|
try:
|
|
namespace = self.get_namespace()
|
|
debug_values = []
|
|
val_dict = pydevd_vars.resolve_compound_var_object_fields(namespace, attributes, self.user_type_renderers)
|
|
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, user_type_renderers=self.user_type_renderers))
|
|
|
|
return debug_values
|
|
except:
|
|
traceback.print_exc()
|
|
raise PythonUnhandledException(traceback.format_exc())
|
|
|
|
def getArray(self, attr, 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 setUserTypeRenderers(self, message):
|
|
try:
|
|
renderers = parse_set_type_renderers_message(message)
|
|
self.user_type_renderers = renderers
|
|
return True
|
|
except:
|
|
traceback.print_exc()
|
|
raise PythonUnhandledException(traceback.format_exc())
|
|
|
|
def execDataViewerAction(self, varName, action, args):
|
|
try:
|
|
tmp_var = pydevd_vars.eval_in_context(varName, self.get_namespace(), self.get_namespace())
|
|
return InternalDataViewerAction.act(tmp_var, action, args.split("\t"))
|
|
|
|
except Exception as e:
|
|
raise PythonUnhandledException(type(e).__name__ + "\n" + traceback.format_exc())
|
|
|
|
def evaluate(self, expression, do_trunc):
|
|
# returns `DebugValue` of evaluated expression
|
|
try:
|
|
namespace = self.get_namespace()
|
|
result = pydevd_vars.eval_in_context(expression, namespace, namespace)
|
|
return [pydevd_thrift.var_to_struct(result, expression, do_trim=do_trunc, user_type_renderers=self.user_type_renderers)]
|
|
except:
|
|
traceback.print_exc()
|
|
raise PythonUnhandledException(traceback.format_exc())
|
|
|
|
def do_get_completions(self, text, act_tok):
|
|
"""Retrieves completion options.
|
|
|
|
Returns the array with completion options tuples.
|
|
|
|
:param text: the full text of the expression to complete
|
|
:param act_tok: resolved part of the expression
|
|
:return: the array of tuples `(name, documentation, args, ret_type)`
|
|
|
|
:Example:
|
|
|
|
Let us execute ``import time`` line in the Python console. Then try
|
|
to complete ``time.sle`` expression. At this point the method would
|
|
receive ``time.sle`` as ``text`` parameter and ``time.`` as
|
|
``act_tok`` parameter. The result would contain the array with the
|
|
following tuple among others: ``[..., ('sleep',
|
|
'sleep(seconds)\\n\\nDelay execution ...', '(seconds)', '2'),
|
|
...]``.
|
|
"""
|
|
try:
|
|
from _pydev_bundle._pydev_completer import Completer
|
|
|
|
completer = Completer(self.get_namespace(), None)
|
|
return completer.complete(act_tok)
|
|
except:
|
|
traceback.print_exc()
|
|
return []
|
|
|
|
def getCompletions(self, text, act_tok):
|
|
try:
|
|
words = self.do_get_completions(text, act_tok)
|
|
return [_to_completion_option(word) for word in words]
|
|
except:
|
|
raise PythonUnhandledException(traceback.format_exc())
|
|
|
|
def loadFullValue(self, seq, scope_attrs):
|
|
"""
|
|
Evaluate full value for async Console variables in a separate thread and send results to IDE side
|
|
:param seq: id of command
|
|
:param scope_attrs: a sequence of variables with their attributes separated by NEXT_VALUE_SEPARATOR
|
|
(i.e.: obj\tattr1\tattr2NEXT_VALUE_SEPARATORobj2\attr1\tattr2)
|
|
:return:
|
|
"""
|
|
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)
|
|
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, self.user_type_renderers)
|
|
t.start()
|
|
except:
|
|
traceback.print_exc()
|
|
raise PythonUnhandledException(traceback.format_exc())
|
|
|
|
def changeVariable(self, attr, value):
|
|
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)
|
|
except:
|
|
traceback.print_exc()
|
|
raise PythonUnhandledException(traceback.format_exc())
|
|
|
|
def _findFrame(self, thread_id, frame_id):
|
|
'''
|
|
Used to show console with variables connection.
|
|
Always return a frame where the locals map to our internal namespace.
|
|
'''
|
|
VIRTUAL_FRAME_ID = "1" # matches PyStackFrameConsole.java
|
|
VIRTUAL_CONSOLE_ID = "console_main" # matches PyThreadConsole.java
|
|
if thread_id == VIRTUAL_CONSOLE_ID and frame_id == VIRTUAL_FRAME_ID:
|
|
f = FakeFrame()
|
|
f.f_globals = {} # As globals=locals here, let's simply let it empty (and save a bit of network traffic).
|
|
f.f_locals = self.get_namespace()
|
|
return f
|
|
else:
|
|
return self.orig_find_frame(thread_id, frame_id)
|
|
|
|
def connectToDebugger(self, debuggerPort, debugger_host=None, debugger_options=None, extra_envs=None):
|
|
'''
|
|
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 = {}
|
|
|
|
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.current_thread(), "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 execTableCommand(self, command, command_type, start_index, end_index, format):
|
|
try:
|
|
try:
|
|
start_index = int(start_index)
|
|
end_index = int(end_index)
|
|
except ValueError:
|
|
start_index = None
|
|
end_index = None
|
|
success, res = exec_table_command(command, command_type,
|
|
start_index, end_index, format,
|
|
self.get_namespace(),
|
|
self.get_namespace())
|
|
if success:
|
|
return res
|
|
except:
|
|
traceback.print_exc()
|
|
raise PythonUnhandledException(traceback.format_exc())
|
|
if not success:
|
|
raise PythonTableException(str(res))
|
|
|
|
|
|
#
|
|
def execTableImageCommand(self, command, command_type, offset, image_id):
|
|
# type: (str, str, int, str) -> Any
|
|
try:
|
|
try:
|
|
offset = int(offset)
|
|
except ValueError:
|
|
offset = None
|
|
success, res = exec_image_table_command(command, command_type, offset, image_id, self.get_namespace(), self.get_namespace())
|
|
if success:
|
|
return res
|
|
except:
|
|
raise PythonUnhandledException(traceback.format_exc())
|
|
if not success:
|
|
raise PythonTableException(str(res))
|
|
|
|
def handshake(self):
|
|
if self.connect_status_queue is not None:
|
|
self.connect_status_queue.put(True)
|
|
return "PyCharm"
|
|
|
|
def get_connect_status_queue(self):
|
|
return self.connect_status_queue
|
|
|
|
def hello(self, input_str):
|
|
# Don't care what the input string is
|
|
return ("Hello eclipse",)
|
|
|
|
|
|
# =======================================================================================================================
|
|
# FakeFrame
|
|
# =======================================================================================================================
|
|
class FakeFrame:
|
|
'''
|
|
Used to show console with variables connection.
|
|
A class to be used as a mock of a frame.
|
|
'''
|