mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
A proper fix for async variables loading in Console (PY-27356)
This commit is contained in:
@@ -6,8 +6,7 @@ from _pydev_bundle._pydev_calltip_util import get_description
|
||||
from _pydev_imps._pydev_saved_modules import thread
|
||||
from _pydevd_bundle import pydevd_vars
|
||||
from _pydevd_bundle import pydevd_xml
|
||||
from _pydevd_bundle.pydevd_constants import IS_JYTHON, dict_iter_items
|
||||
from _pydevd_bundle.pydevd_utils import to_string
|
||||
from _pydevd_bundle.pydevd_constants import IS_JYTHON, dict_iter_items, NEXT_VALUE_SEPARATOR
|
||||
|
||||
try:
|
||||
import cStringIO as StringIO #may not always be available @UnusedImport
|
||||
@@ -484,14 +483,13 @@ class BaseInterpreterInterface:
|
||||
def getVariable(self, attributes):
|
||||
xml = StringIO.StringIO()
|
||||
xml.write("<xml>")
|
||||
valDict = pydevd_vars.resolve_var(self.get_namespace(), attributes)
|
||||
if valDict is None:
|
||||
valDict = {}
|
||||
|
||||
keys = valDict.keys()
|
||||
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 = valDict[k]
|
||||
val = val_dict[k]
|
||||
evaluate_full_value = pydevd_xml.should_evaluate_full_value(val)
|
||||
xml.write(pydevd_vars.var_to_xml(val, k, evaluate_full_value=evaluate_full_value))
|
||||
|
||||
@@ -512,14 +510,34 @@ class BaseInterpreterInterface:
|
||||
xml.write("</xml>")
|
||||
return xml.getvalue()
|
||||
|
||||
def loadFullValue(self, expressions):
|
||||
xml = StringIO.StringIO()
|
||||
xml.write("<xml>")
|
||||
for expression in expressions:
|
||||
result = pydevd_vars.eval_in_context(expression, self.get_namespace(), self.get_namespace())
|
||||
xml.write(pydevd_vars.var_to_xml(result, expression, evaluate_full_value=True))
|
||||
xml.write("</xml>")
|
||||
return xml.getvalue()
|
||||
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:
|
||||
"""
|
||||
frame_variables = self.get_namespace()
|
||||
var_objects = []
|
||||
vars = scope_attrs.split(NEXT_VALUE_SEPARATOR)
|
||||
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))
|
||||
|
||||
from _pydevd_bundle.pydevd_comm import GetValueAsyncThreadConsole
|
||||
t = GetValueAsyncThreadConsole(self.get_server(), seq, var_objects)
|
||||
t.start()
|
||||
|
||||
def changeVariable(self, attr, value):
|
||||
def do_change_variable():
|
||||
|
||||
@@ -1026,7 +1026,7 @@ class InternalGetVariable(InternalThreadCommand):
|
||||
try:
|
||||
xml = StringIO.StringIO()
|
||||
xml.write("<xml>")
|
||||
_typeName, val_dict = pydevd_vars.resolve_compound_variable(self.thread_id, self.frame_id, self.scope, self.attributes)
|
||||
_typeName, val_dict = pydevd_vars.resolve_compound_variable_fields(self.thread_id, self.frame_id, self.scope, self.attributes)
|
||||
if val_dict is None:
|
||||
val_dict = {}
|
||||
|
||||
@@ -1482,11 +1482,10 @@ class InternalLoadFullValue(InternalThreadCommand):
|
||||
else:
|
||||
scope, attrs = (variable, None)
|
||||
name = scope
|
||||
|
||||
var_obj = pydevd_vars.getVariable(self.thread_id, self.frame_id, scope, attrs)
|
||||
var_objects.append((var_obj, name))
|
||||
|
||||
t = GetValueAsyncThread(dbg, self.thread_id, self.frame_id, self.sequence, var_objects)
|
||||
t = GetValueAsyncThreadDebug(dbg, self.sequence, var_objects)
|
||||
t.start()
|
||||
except:
|
||||
exc = get_exception_traceback_str()
|
||||
@@ -1495,16 +1494,20 @@ class InternalLoadFullValue(InternalThreadCommand):
|
||||
dbg.writer.add_command(cmd)
|
||||
|
||||
|
||||
class GetValueAsyncThread(PyDBDaemonThread):
|
||||
def __init__(self, py_db, thread_id, frame_id, seq, var_objects):
|
||||
class AbstractGetValueAsyncThread(PyDBDaemonThread):
|
||||
"""
|
||||
Abstract class for a thread, which evaluates values for async variables
|
||||
"""
|
||||
def __init__(self, frame_accessor, seq, var_objects):
|
||||
PyDBDaemonThread.__init__(self)
|
||||
self.py_db = py_db
|
||||
self.thread_id = thread_id
|
||||
self.frame_id = frame_id
|
||||
self.frame_accessor = frame_accessor
|
||||
self.seq = seq
|
||||
self.var_objs = var_objects
|
||||
self.cancel_event = threading.Event()
|
||||
|
||||
def send_result(self, xml):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _on_run(self):
|
||||
start = time.time()
|
||||
xml = StringIO.StringIO()
|
||||
@@ -1515,9 +1518,29 @@ class GetValueAsyncThread(PyDBDaemonThread):
|
||||
break
|
||||
xml.write(pydevd_xml.var_to_xml(var_obj, name, evaluate_full_value=True))
|
||||
xml.write("</xml>")
|
||||
cmd = self.py_db.cmd_factory.make_load_full_value_message(self.seq, xml.getvalue())
|
||||
self.send_result(xml)
|
||||
xml.close()
|
||||
self.py_db.writer.add_command(cmd)
|
||||
|
||||
|
||||
class GetValueAsyncThreadDebug(AbstractGetValueAsyncThread):
|
||||
"""
|
||||
A thread for evaluation async values, which returns result for debugger
|
||||
Create message and send it via writer thread
|
||||
"""
|
||||
def send_result(self, xml):
|
||||
if self.frame_accessor is not None:
|
||||
cmd = self.frame_accessor.cmd_factory.make_load_full_value_message(self.seq, xml.getvalue())
|
||||
self.frame_accessor.writer.add_command(cmd)
|
||||
|
||||
|
||||
class GetValueAsyncThreadConsole(AbstractGetValueAsyncThread):
|
||||
"""
|
||||
A thread for evaluation async values, which returns result for Console
|
||||
Send result directly to Console's server
|
||||
"""
|
||||
def send_result(self, xml):
|
||||
if self.frame_accessor is not None:
|
||||
self.frame_accessor.ReturnFullValue(self.seq, xml.getvalue())
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
|
||||
@@ -229,8 +229,17 @@ def getVariable(thread_id, frame_id, scope, attrs):
|
||||
return var
|
||||
|
||||
|
||||
def resolve_compound_variable(thread_id, frame_id, scope, attrs):
|
||||
""" returns the value of the compound variable as a dictionary"""
|
||||
def resolve_compound_variable_fields(thread_id, frame_id, scope, attrs):
|
||||
"""
|
||||
Resolve compound variable in debugger scopes by its name and attributes
|
||||
|
||||
:param thread_id: id of the variable's thread
|
||||
:param frame_id: id of the variable's frame
|
||||
:param scope: can be BY_ID, EXPRESSION, GLOBAL, LOCAL, FRAME
|
||||
:param attrs: after reaching the proper scope, we have to get the attributes until we find
|
||||
the proper location (i.e.: obj\tattr1\tattr2)
|
||||
:return: a dictionary of variables's fields
|
||||
"""
|
||||
|
||||
var = getVariable(thread_id, frame_id, scope, attrs)
|
||||
|
||||
@@ -243,12 +252,36 @@ def resolve_compound_variable(thread_id, frame_id, scope, attrs):
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def resolve_var(var, attrs):
|
||||
attrList = attrs.split('\t')
|
||||
def resolve_var_object(var, attrs):
|
||||
"""
|
||||
Resolve variable's attribute
|
||||
|
||||
for k in attrList:
|
||||
:param var: an object of variable
|
||||
:param attrs: a sequence of variable's attributes separated by \t (i.e.: obj\tattr1\tattr2)
|
||||
:return: a value of resolved variable's attribute
|
||||
"""
|
||||
if attrs is not None:
|
||||
attr_list = attrs.split('\t')
|
||||
else:
|
||||
attr_list = []
|
||||
for k in attr_list:
|
||||
type, _typeName, resolver = get_type(var)
|
||||
var = resolver.resolve(var, k)
|
||||
return var
|
||||
|
||||
|
||||
def resolve_compound_var_object_fields(var, attrs):
|
||||
"""
|
||||
Resolve compound variable by its object and attributes
|
||||
|
||||
:param var: an object of variable
|
||||
:param attrs: a sequence of variable's attributes separated by \t (i.e.: obj\tattr1\tattr2)
|
||||
:return: a dictionary of variables's fields
|
||||
"""
|
||||
attr_list = attrs.split('\t')
|
||||
|
||||
for k in attr_list:
|
||||
type, _typeName, resolver = get_type(var)
|
||||
var = resolver.resolve(var, k)
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user