mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Show globals, group variables and show all dir() variables.
Fixes https://github.com/microsoft/ptvsd/issues/118 Fixes https://github.com/microsoft/ptvsd/issues/763 Fixes https://github.com/microsoft/ptvsd/issues/1621
This commit is contained in:
parent
ade162b1d3
commit
d2014babd6
19 changed files with 603 additions and 148 deletions
|
|
@ -490,8 +490,7 @@ class BaseInterpreterInterface:
|
|||
if val_dict is None:
|
||||
val_dict = {}
|
||||
|
||||
keys = val_dict.keys()
|
||||
for k in keys:
|
||||
for k, val in dict_iter_items(val_dict):
|
||||
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))
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import ctypes
|
|||
from _pydevd_bundle.pydevd_collect_bytecode_info import code_to_bytecode_representation
|
||||
import itertools
|
||||
import linecache
|
||||
from _pydevd_bundle.pydevd_utils import DAPGrouper
|
||||
|
||||
try:
|
||||
import dis
|
||||
|
|
@ -65,6 +66,19 @@ else:
|
|||
|
||||
class PyDevdAPI(object):
|
||||
|
||||
class VariablePresentation(object):
|
||||
|
||||
def __init__(self, special='group', function='group', class_='group', protected='inline'):
|
||||
self._presentation = {
|
||||
DAPGrouper.SCOPE_SPECIAL_VARS: special,
|
||||
DAPGrouper.SCOPE_FUNCTION_VARS: function,
|
||||
DAPGrouper.SCOPE_CLASS_VARS: class_,
|
||||
DAPGrouper.SCOPE_PROTECTED_VARS: protected,
|
||||
}
|
||||
|
||||
def get_presentation(self, scope):
|
||||
return self._presentation[scope]
|
||||
|
||||
def run(self, py_db):
|
||||
py_db.ready_to_run = True
|
||||
|
||||
|
|
@ -840,6 +854,10 @@ class PyDevdAPI(object):
|
|||
self.reapply_breakpoints(py_db)
|
||||
return ''
|
||||
|
||||
def set_variable_presentation(self, py_db, variable_presentation):
|
||||
assert isinstance(variable_presentation, self.VariablePresentation)
|
||||
py_db.variable_presentation = variable_presentation
|
||||
|
||||
def get_ppid(self):
|
||||
'''
|
||||
Provides the parent pid (even for older versions of Python on Windows).
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ from _pydevd_bundle import pydevd_vm_type
|
|||
import sys
|
||||
import traceback
|
||||
from _pydevd_bundle.pydevd_utils import quote_smart as quote, compare_object_attrs_key, \
|
||||
notify_about_gevent_if_needed, isinstance_checked
|
||||
notify_about_gevent_if_needed, isinstance_checked, ScopeRequest
|
||||
from _pydev_bundle import pydev_log
|
||||
from _pydev_bundle.pydev_log import exception as pydev_log_exception
|
||||
from _pydev_bundle import _pydev_completer
|
||||
|
|
@ -729,6 +729,11 @@ def internal_get_variable_json(py_db, request):
|
|||
'''
|
||||
arguments = request.arguments # : :type arguments: VariablesArguments
|
||||
variables_reference = arguments.variablesReference
|
||||
scope = None
|
||||
if isinstance_checked(variables_reference, ScopeRequest):
|
||||
scope = variables_reference
|
||||
variables_reference = variables_reference.variable_reference
|
||||
|
||||
fmt = arguments.format
|
||||
if hasattr(fmt, 'to_dict'):
|
||||
fmt = fmt.to_dict()
|
||||
|
|
@ -739,7 +744,7 @@ def internal_get_variable_json(py_db, request):
|
|||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
for child_var in variable.get_children_variables(fmt=fmt):
|
||||
for child_var in variable.get_children_variables(fmt=fmt, scope=scope):
|
||||
variables.append(child_var.get_var_data(fmt=fmt))
|
||||
|
||||
body = VariablesResponseBody(variables)
|
||||
|
|
@ -844,6 +849,11 @@ def internal_change_variable_json(py_db, request):
|
|||
# : :type arguments: SetVariableArguments
|
||||
arguments = request.arguments
|
||||
variables_reference = arguments.variablesReference
|
||||
scope = None
|
||||
if isinstance_checked(variables_reference, ScopeRequest):
|
||||
scope = variables_reference
|
||||
variables_reference = variables_reference.variable_reference
|
||||
|
||||
fmt = arguments.format
|
||||
if hasattr(fmt, 'to_dict'):
|
||||
fmt = fmt.to_dict()
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ try:
|
|||
except NameError:
|
||||
int_types = (int,)
|
||||
|
||||
# types does not include a MethodWrapperType
|
||||
try:
|
||||
MethodWrapperType = type([].__str__)
|
||||
except:
|
||||
MethodWrapperType = None
|
||||
|
||||
import sys # Note: the sys import must be here anyways (others depend on it)
|
||||
|
||||
# Preload codecs to avoid imports to them later on which can potentially halt the debugger.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ from _pydevd_bundle.pydevd_comm_constants import (
|
|||
from _pydevd_bundle.pydevd_filtering import ExcludeFilter
|
||||
from _pydevd_bundle.pydevd_json_debug_options import _extract_debug_options, DebugOptions
|
||||
from _pydevd_bundle.pydevd_net_command import NetCommand
|
||||
from _pydevd_bundle.pydevd_utils import convert_dap_log_message_to_expression
|
||||
from _pydevd_bundle.pydevd_utils import convert_dap_log_message_to_expression, ScopeRequest
|
||||
from _pydevd_bundle.pydevd_constants import (PY_IMPL_NAME, DebugInfoHolder, PY_VERSION_STR,
|
||||
PY_IMPL_VERSION_STR, IS_64BIT_PROCESS)
|
||||
from _pydevd_bundle.pydevd_trace_dispatch import USING_CYTHON
|
||||
|
|
@ -162,7 +162,7 @@ class PyDevJsonCommandProcessor(object):
|
|||
else:
|
||||
if DebugInfoHolder.DEBUG_RECORD_SOCKET_READS and DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1:
|
||||
pydev_log.info('Process %s: %s\n' % (
|
||||
request.__class__.__name__, json.dumps(request.to_dict(), indent=4, sort_keys=True),))
|
||||
request.__class__.__name__, json.dumps(request.to_dict(update_ids_to_dap=True), indent=4, sort_keys=True),))
|
||||
|
||||
assert request.type == 'request'
|
||||
method_name = 'on_%s_request' % (request.command.lower(),)
|
||||
|
|
@ -328,6 +328,33 @@ class PyDevJsonCommandProcessor(object):
|
|||
terminate_child_processes = args.get('terminateChildProcesses', True)
|
||||
self.api.set_terminate_child_processes(py_db, terminate_child_processes)
|
||||
|
||||
variable_presentation = args.get('variablePresentation', None)
|
||||
if isinstance(variable_presentation, dict):
|
||||
|
||||
def get_variable_presentation(setting, default):
|
||||
value = variable_presentation.get(setting, default)
|
||||
if value not in ('group', 'inline', 'hide'):
|
||||
pydev_log.info(
|
||||
'The value set for "%s" (%s) in the variablePresentation is not valid. Valid values are: "group", "inline", "hide"' % (
|
||||
setting, value,))
|
||||
value = default
|
||||
|
||||
return value
|
||||
|
||||
default = get_variable_presentation('all', 'group')
|
||||
|
||||
special_presentation = get_variable_presentation('special', default)
|
||||
function_presentation = get_variable_presentation('function', default)
|
||||
class_presentation = get_variable_presentation('class', default)
|
||||
protected_presentation = get_variable_presentation('protected', default)
|
||||
|
||||
self.api.set_variable_presentation(py_db, self.api.VariablePresentation(
|
||||
special_presentation,
|
||||
function_presentation,
|
||||
class_presentation,
|
||||
protected_presentation
|
||||
))
|
||||
|
||||
exclude_filters = []
|
||||
|
||||
if rules is not None:
|
||||
|
|
@ -747,7 +774,10 @@ class PyDevJsonCommandProcessor(object):
|
|||
frame_id = request.arguments.frameId
|
||||
|
||||
variables_reference = frame_id
|
||||
scopes = [Scope('Locals', int(variables_reference), False).to_dict()]
|
||||
scopes = [
|
||||
Scope('Locals', ScopeRequest(int(variables_reference), 'locals'), False),
|
||||
Scope('Globals', ScopeRequest(int(variables_reference), 'globals'), False),
|
||||
]
|
||||
body = ScopesResponseBody(scopes)
|
||||
scopes_response = pydevd_base_schema.build_response(request, kwargs={'body': body})
|
||||
return NetCommand(CMD_RETURN, 0, scopes_response, is_json=True)
|
||||
|
|
@ -817,6 +847,9 @@ class PyDevJsonCommandProcessor(object):
|
|||
arguments = request.arguments # : :type arguments: VariablesArguments
|
||||
variables_reference = arguments.variablesReference
|
||||
|
||||
if isinstance(variables_reference, ScopeRequest):
|
||||
variables_reference = variables_reference.variable_reference
|
||||
|
||||
thread_id = py_db.suspended_frames_manager.get_thread_id_for_variable_reference(
|
||||
variables_reference)
|
||||
if thread_id is not None:
|
||||
|
|
@ -835,6 +868,9 @@ class PyDevJsonCommandProcessor(object):
|
|||
arguments = request.arguments # : :type arguments: SetVariableArguments
|
||||
variables_reference = arguments.variablesReference
|
||||
|
||||
if isinstance(variables_reference, ScopeRequest):
|
||||
variables_reference = variables_reference.variable_reference
|
||||
|
||||
if arguments.name.startswith('(return) '):
|
||||
response = pydevd_base_schema.build_response(
|
||||
request,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from _pydev_bundle import pydev_log
|
||||
from _pydevd_bundle.pydevd_utils import hasattr_checked
|
||||
from _pydevd_bundle.pydevd_utils import hasattr_checked, DAPGrouper
|
||||
try:
|
||||
import StringIO
|
||||
except:
|
||||
|
|
@ -8,7 +8,8 @@ import traceback
|
|||
from os.path import basename
|
||||
|
||||
from functools import partial
|
||||
from _pydevd_bundle.pydevd_constants import dict_iter_items, dict_keys, xrange, IS_PY36_OR_GREATER
|
||||
from _pydevd_bundle.pydevd_constants import dict_iter_items, dict_keys, xrange, IS_PY36_OR_GREATER, \
|
||||
MethodWrapperType, RETURN_VALUES_DICT, DebugInfoHolder, IS_PYPY
|
||||
from _pydevd_bundle.pydevd_safe_repr import SafeRepr
|
||||
|
||||
# Note: 300 is already a lot to see in the outline (after that the user should really use the shell to get things)
|
||||
|
|
@ -26,23 +27,6 @@ class UnableToResolveVariableException(Exception):
|
|||
pass
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# InspectStub
|
||||
#=======================================================================================================================
|
||||
class InspectStub:
|
||||
|
||||
def isbuiltin(self, _args):
|
||||
return False
|
||||
|
||||
def isroutine(self, object):
|
||||
return False
|
||||
|
||||
|
||||
try:
|
||||
import inspect
|
||||
except:
|
||||
inspect = InspectStub()
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except:
|
||||
|
|
@ -53,12 +37,6 @@ try:
|
|||
except:
|
||||
pass
|
||||
|
||||
# types does not include a MethodWrapperType
|
||||
try:
|
||||
MethodWrapperType = type([].__str__)
|
||||
except:
|
||||
MethodWrapperType = None
|
||||
|
||||
#=======================================================================================================================
|
||||
# See: pydevd_extension_api module for resolver interface
|
||||
#=======================================================================================================================
|
||||
|
|
@ -99,9 +77,16 @@ class DefaultResolver:
|
|||
|
||||
lst = sorted(dict_iter_items(dct), key=lambda tup: sorted_attributes_key(tup[0]))
|
||||
if used___dict__:
|
||||
return [(attr_name, attr_value, '.__dict__[%s]' % attr_name) for (attr_name, attr_value) in lst]
|
||||
eval_name = '.__dict__[%s]'
|
||||
else:
|
||||
return [(attr_name, attr_value, '.%s' % attr_name) for (attr_name, attr_value) in lst]
|
||||
eval_name = '.%s'
|
||||
|
||||
ret = []
|
||||
for attr_name, attr_value in lst:
|
||||
entry = (attr_name, attr_value, eval_name % attr_name)
|
||||
ret.append(entry)
|
||||
|
||||
return ret
|
||||
|
||||
def get_dictionary(self, var, names=None, used___dict__=False):
|
||||
if MethodWrapperType:
|
||||
|
|
@ -183,11 +168,12 @@ class DefaultResolver:
|
|||
using obj.__dict__[name] instead of getattr(obj, name)
|
||||
'''
|
||||
|
||||
# TODO: Those should be options (would fix https://github.com/Microsoft/ptvsd/issues/66).
|
||||
filter_private = False
|
||||
filter_special = True
|
||||
filter_function = True
|
||||
filter_builtin = True
|
||||
# On PyPy we never show functions. This is because of a corner case where PyPy becomes
|
||||
# absurdly slow -- it takes almost half a second to introspect a single numpy function (so,
|
||||
# the related test, "test_case_16_resolve_numpy_array", times out... this probably isn't
|
||||
# specific to numpy, but to any library where the CPython bridge is used, but as we
|
||||
# can't be sure in the debugger, we play it safe and don't show it at all).
|
||||
filter_function = IS_PYPY
|
||||
|
||||
if not names:
|
||||
names, used___dict__ = self.get_names(var)
|
||||
|
|
@ -197,45 +183,38 @@ class DefaultResolver:
|
|||
# optimize the operation by removing as many items as possible in the
|
||||
# first filters, leaving fewer items for later filters
|
||||
|
||||
if filter_builtin or filter_function:
|
||||
for name in names:
|
||||
try:
|
||||
name_as_str = name
|
||||
if name_as_str.__class__ != str:
|
||||
name_as_str = '%r' % (name_as_str,)
|
||||
for name in names:
|
||||
try:
|
||||
name_as_str = name
|
||||
if name_as_str.__class__ != str:
|
||||
name_as_str = '%r' % (name_as_str,)
|
||||
|
||||
if filter_special:
|
||||
if name_as_str.startswith('__') and name_as_str.endswith('__'):
|
||||
continue
|
||||
if not used___dict__:
|
||||
attr = getattr(var, name)
|
||||
else:
|
||||
attr = var.__dict__[name]
|
||||
|
||||
if filter_private:
|
||||
if name_as_str.startswith('_') or name_as_str.endswith('__'):
|
||||
continue
|
||||
if not used___dict__:
|
||||
attr = getattr(var, name)
|
||||
else:
|
||||
attr = var.__dict__[name]
|
||||
# filter functions?
|
||||
if filter_function:
|
||||
if inspect.isroutine(attr) or isinstance(attr, MethodWrapperType):
|
||||
continue
|
||||
except:
|
||||
# if some error occurs getting it, let's put it to the user.
|
||||
strIO = StringIO.StringIO()
|
||||
traceback.print_exc(file=strIO)
|
||||
attr = strIO.getvalue()
|
||||
|
||||
# filter builtins?
|
||||
if filter_builtin:
|
||||
if inspect.isbuiltin(attr):
|
||||
continue
|
||||
|
||||
# filter functions?
|
||||
if filter_function:
|
||||
if inspect.isroutine(attr) or isinstance(attr, MethodWrapperType):
|
||||
continue
|
||||
except:
|
||||
# if some error occurs getting it, let's put it to the user.
|
||||
strIO = StringIO.StringIO()
|
||||
traceback.print_exc(file=strIO)
|
||||
attr = strIO.getvalue()
|
||||
|
||||
d[name_as_str] = attr
|
||||
d[name_as_str] = attr
|
||||
|
||||
return d, used___dict__
|
||||
|
||||
|
||||
class DAPGrouperResolver:
|
||||
|
||||
def get_contents_debug_adapter_protocol(self, obj, fmt=None):
|
||||
return obj.get_contents_debug_adapter_protocol()
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# DictResolver
|
||||
#=======================================================================================================================
|
||||
|
|
@ -322,10 +301,10 @@ class DictResolver:
|
|||
ret[TOO_LARGE_ATTR] = TOO_LARGE_MSG
|
||||
break
|
||||
|
||||
ret['__len__'] = len(dict)
|
||||
# in case if the class extends built-in type and has some additional fields
|
||||
additional_fields = defaultResolver.get_dictionary(dict)
|
||||
ret.update(additional_fields)
|
||||
ret['__len__'] = len(dict)
|
||||
return ret
|
||||
|
||||
|
||||
|
|
@ -396,10 +375,10 @@ class TupleResolver: # to enumerate tuples and lists
|
|||
d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
|
||||
break
|
||||
|
||||
d['__len__'] = len(var)
|
||||
# in case if the class extends built-in type and has some additional fields
|
||||
additional_fields = defaultResolver.get_dictionary(var)
|
||||
d.update(additional_fields)
|
||||
d['__len__'] = len(var)
|
||||
return d
|
||||
|
||||
|
||||
|
|
@ -452,10 +431,10 @@ class SetResolver:
|
|||
d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
|
||||
break
|
||||
|
||||
d['__len__'] = len(var)
|
||||
# in case if the class extends built-in type and has some additional fields
|
||||
additional_fields = defaultResolver.get_dictionary(var)
|
||||
d.update(additional_fields)
|
||||
d['__len__'] = len(var)
|
||||
return d
|
||||
|
||||
def change_var_from_name(self, container, name, new_value):
|
||||
|
|
@ -650,3 +629,56 @@ djangoFormResolver = DjangoFormResolver()
|
|||
dequeResolver = DequeResolver()
|
||||
orderedDictResolver = OrderedDictResolver()
|
||||
frameResolver = FrameResolver()
|
||||
dapGrouperResolver = DAPGrouperResolver()
|
||||
|
||||
|
||||
class InspectStub:
|
||||
|
||||
def isbuiltin(self, _args):
|
||||
return False
|
||||
|
||||
def isroutine(self, object):
|
||||
return False
|
||||
|
||||
|
||||
try:
|
||||
import inspect
|
||||
except:
|
||||
inspect = InspectStub()
|
||||
|
||||
|
||||
def get_var_scope(attr_name, attr_value, evaluate_name, handle_return_values):
|
||||
if attr_name.startswith("'"):
|
||||
if attr_name.endswith("'"):
|
||||
attr_name = attr_name[1:-1]
|
||||
else:
|
||||
i = attr_name.find("__' (")
|
||||
if i >= 0:
|
||||
# Handle attr_name such as: >>'__name__' (1732494379184)<<
|
||||
attr_name = attr_name[1: i + 2]
|
||||
|
||||
if handle_return_values and attr_name == RETURN_VALUES_DICT:
|
||||
return ''
|
||||
|
||||
elif attr_name == '__len__' and evaluate_name != '.__len__':
|
||||
# Treat the __len__ we generate internally separate from the __len__ function
|
||||
return ''
|
||||
|
||||
if attr_name.startswith('__') and attr_name.endswith('__'):
|
||||
return DAPGrouper.SCOPE_SPECIAL_VARS
|
||||
|
||||
if attr_name.startswith('_') or attr_name.endswith('__'):
|
||||
return DAPGrouper.SCOPE_PROTECTED_VARS
|
||||
|
||||
try:
|
||||
if inspect.isroutine(attr_value) or isinstance(attr_value, MethodWrapperType):
|
||||
return DAPGrouper.SCOPE_FUNCTION_VARS
|
||||
|
||||
elif inspect.isclass(attr_value):
|
||||
return DAPGrouper.SCOPE_CLASS_VARS
|
||||
except:
|
||||
# It's possible that isinstance throws an exception when dealing with user-code.
|
||||
if DebugInfoHolder.DEBUG_TRACE_LEVEL > 0:
|
||||
pydev_log.exception()
|
||||
|
||||
return ''
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ from _pydevd_bundle.pydevd_constants import get_frame, dict_items, RETURN_VALUES
|
|||
dict_iter_items, ForkSafeLock
|
||||
from _pydevd_bundle.pydevd_xml import get_variable_details, get_type
|
||||
from _pydev_bundle.pydev_override import overrides
|
||||
from _pydevd_bundle.pydevd_resolver import sorted_attributes_key, TOO_LARGE_ATTR
|
||||
from _pydevd_bundle.pydevd_resolver import sorted_attributes_key, TOO_LARGE_ATTR, get_var_scope
|
||||
from _pydevd_bundle.pydevd_safe_repr import SafeRepr
|
||||
from _pydev_bundle import pydev_log
|
||||
from _pydevd_bundle import pydevd_vars
|
||||
from _pydev_bundle.pydev_imports import Exec
|
||||
from _pydevd_bundle.pydevd_frame_utils import FramesList
|
||||
from _pydevd_bundle.pydevd_utils import ScopeRequest, DAPGrouper
|
||||
|
||||
|
||||
class _AbstractVariable(object):
|
||||
|
|
@ -21,6 +22,10 @@ class _AbstractVariable(object):
|
|||
value = None
|
||||
evaluate_name = None
|
||||
|
||||
def __init__(self, py_db):
|
||||
assert py_db is not None
|
||||
self.py_db = py_db
|
||||
|
||||
def get_name(self):
|
||||
return self.name
|
||||
|
||||
|
|
@ -78,20 +83,61 @@ class _AbstractVariable(object):
|
|||
|
||||
return var_data
|
||||
|
||||
def get_children_variables(self, fmt=None):
|
||||
def get_children_variables(self, fmt=None, scope=None):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_child_variable_named(self, name, fmt=None):
|
||||
for child_var in self.get_children_variables(fmt=fmt):
|
||||
def get_child_variable_named(self, name, fmt=None, scope=None):
|
||||
for child_var in self.get_children_variables(fmt=fmt, scope=scope):
|
||||
if child_var.get_name() == name:
|
||||
return child_var
|
||||
return None
|
||||
|
||||
def _group_entries(self, lst, handle_return_values):
|
||||
scope_to_grouper = {}
|
||||
|
||||
group_entries = []
|
||||
if isinstance(self.value, DAPGrouper):
|
||||
new_lst = lst
|
||||
else:
|
||||
new_lst = []
|
||||
get_presentation = self.py_db.variable_presentation.get_presentation
|
||||
# Now that we have the contents, group items.
|
||||
for attr_name, attr_value, evaluate_name in lst:
|
||||
scope = get_var_scope(attr_name, attr_value, evaluate_name, handle_return_values)
|
||||
|
||||
entry = (attr_name, attr_value, evaluate_name)
|
||||
if scope:
|
||||
presentation = get_presentation(scope)
|
||||
if presentation == 'hide':
|
||||
continue
|
||||
|
||||
elif presentation == 'inline':
|
||||
new_lst.append(entry)
|
||||
|
||||
else: # group
|
||||
if scope not in scope_to_grouper:
|
||||
grouper = DAPGrouper(scope)
|
||||
scope_to_grouper[scope] = grouper
|
||||
else:
|
||||
grouper = scope_to_grouper[scope]
|
||||
|
||||
grouper.contents_debug_adapter_protocol.append(entry)
|
||||
|
||||
else:
|
||||
new_lst.append(entry)
|
||||
|
||||
for scope in DAPGrouper.SCOPES_SORTED:
|
||||
grouper = scope_to_grouper.get(scope)
|
||||
if grouper is not None:
|
||||
group_entries.append((scope, grouper, None))
|
||||
|
||||
return new_lst, group_entries
|
||||
|
||||
|
||||
class _ObjectVariable(_AbstractVariable):
|
||||
|
||||
def __init__(self, name, value, register_variable, is_return_value=False, evaluate_name=None, frame=None):
|
||||
_AbstractVariable.__init__(self)
|
||||
def __init__(self, py_db, name, value, register_variable, is_return_value=False, evaluate_name=None, frame=None):
|
||||
_AbstractVariable.__init__(self, py_db)
|
||||
self.frame = frame
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
|
@ -101,7 +147,7 @@ class _ObjectVariable(_AbstractVariable):
|
|||
self.evaluate_name = evaluate_name
|
||||
|
||||
@overrides(_AbstractVariable.get_children_variables)
|
||||
def get_children_variables(self, fmt=None):
|
||||
def get_children_variables(self, fmt=None, scope=None):
|
||||
_type, _type_name, resolver = get_type(self.value)
|
||||
|
||||
children_variables = []
|
||||
|
|
@ -117,6 +163,9 @@ class _ObjectVariable(_AbstractVariable):
|
|||
# No evaluate name in this case.
|
||||
lst = [(key, value, None) for (key, value) in lst]
|
||||
|
||||
lst, group_entries = self._group_entries(lst, handle_return_values=False)
|
||||
if group_entries:
|
||||
lst = group_entries + lst
|
||||
parent_evaluate_name = self.evaluate_name
|
||||
if parent_evaluate_name:
|
||||
for key, val, evaluate_name in lst:
|
||||
|
|
@ -126,12 +175,12 @@ class _ObjectVariable(_AbstractVariable):
|
|||
else:
|
||||
evaluate_name = parent_evaluate_name + evaluate_name
|
||||
variable = _ObjectVariable(
|
||||
key, val, self._register_variable, evaluate_name=evaluate_name, frame=self.frame)
|
||||
self.py_db, key, val, self._register_variable, evaluate_name=evaluate_name, frame=self.frame)
|
||||
children_variables.append(variable)
|
||||
else:
|
||||
for key, val, evaluate_name in lst:
|
||||
# No evaluate name
|
||||
variable = _ObjectVariable(key, val, self._register_variable, frame=self.frame)
|
||||
variable = _ObjectVariable(self.py_db, key, val, self._register_variable, frame=self.frame)
|
||||
children_variables.append(variable)
|
||||
|
||||
return children_variables
|
||||
|
|
@ -159,7 +208,7 @@ class _ObjectVariable(_AbstractVariable):
|
|||
new_key = container_resolver.change_var_from_name(self.value, name, new_value)
|
||||
if new_key is not None:
|
||||
return _ObjectVariable(
|
||||
new_key, new_value, self._register_variable, evaluate_name=None, frame=self.frame)
|
||||
self.py_db, new_key, new_value, self._register_variable, evaluate_name=None, frame=self.frame)
|
||||
|
||||
return None
|
||||
else:
|
||||
|
|
@ -184,8 +233,8 @@ def sorted_variables_key(obj):
|
|||
|
||||
class _FrameVariable(_AbstractVariable):
|
||||
|
||||
def __init__(self, frame, register_variable):
|
||||
_AbstractVariable.__init__(self)
|
||||
def __init__(self, py_db, frame, register_variable):
|
||||
_AbstractVariable.__init__(self, py_db)
|
||||
self.frame = frame
|
||||
|
||||
self.name = self.frame.f_code.co_name
|
||||
|
|
@ -202,21 +251,44 @@ class _FrameVariable(_AbstractVariable):
|
|||
return self.get_child_variable_named(name, fmt=fmt)
|
||||
|
||||
@overrides(_AbstractVariable.get_children_variables)
|
||||
def get_children_variables(self, fmt=None):
|
||||
def get_children_variables(self, fmt=None, scope=None):
|
||||
children_variables = []
|
||||
for key, val in dict_items(self.frame.f_locals):
|
||||
if scope is not None:
|
||||
assert isinstance(scope, ScopeRequest)
|
||||
scope = scope.scope
|
||||
|
||||
if scope in ('locals', None):
|
||||
dct = self.frame.f_locals
|
||||
elif scope == 'globals':
|
||||
dct = self.frame.f_globals
|
||||
else:
|
||||
raise AssertionError('Unexpected scope: %s' % (scope,))
|
||||
|
||||
lst, group_entries = self._group_entries([(x[0], x[1], None) for x in dict_items(dct) if x[0] != '_pydev_stop_at_break'], handle_return_values=True)
|
||||
group_variables = []
|
||||
|
||||
for key, val, _ in group_entries:
|
||||
# Make sure that the contents in the group are also sorted.
|
||||
val.contents_debug_adapter_protocol.sort(key=lambda v:sorted_attributes_key(v[0]))
|
||||
variable = _ObjectVariable(self.py_db, key, val, self._register_variable, False, key, frame=self.frame)
|
||||
group_variables.append(variable)
|
||||
|
||||
for key, val, _ in lst:
|
||||
is_return_value = key == RETURN_VALUES_DICT
|
||||
if is_return_value:
|
||||
for return_key, return_value in dict_iter_items(val):
|
||||
variable = _ObjectVariable(
|
||||
return_key, return_value, self._register_variable, is_return_value, '%s[%r]' % (key, return_key), frame=self.frame)
|
||||
self.py_db, return_key, return_value, self._register_variable, is_return_value, '%s[%r]' % (key, return_key), frame=self.frame)
|
||||
children_variables.append(variable)
|
||||
else:
|
||||
variable = _ObjectVariable(key, val, self._register_variable, is_return_value, key, frame=self.frame)
|
||||
variable = _ObjectVariable(self.py_db, key, val, self._register_variable, is_return_value, key, frame=self.frame)
|
||||
children_variables.append(variable)
|
||||
|
||||
# Frame variables always sorted.
|
||||
children_variables.sort(key=sorted_variables_key)
|
||||
if group_variables:
|
||||
# Groups have priority over other variables.
|
||||
children_variables = group_variables + children_variables
|
||||
|
||||
return children_variables
|
||||
|
||||
|
|
@ -270,7 +342,7 @@ class _FramesTracker(object):
|
|||
|
||||
# Still not created, let's do it now.
|
||||
return _ObjectVariable(
|
||||
name, value, self._register_variable, is_return_value=False, evaluate_name=evaluate_name, frame=frame)
|
||||
self.py_db, name, value, self._register_variable, is_return_value=False, evaluate_name=evaluate_name, frame=frame)
|
||||
|
||||
def get_main_thread_id(self):
|
||||
return self._main_thread_id
|
||||
|
|
@ -306,7 +378,7 @@ class _FramesTracker(object):
|
|||
for frame in frames_list:
|
||||
frame_id = id(frame)
|
||||
self._frame_id_to_frame[frame_id] = frame
|
||||
_FrameVariable(frame, self._register_variable) # Instancing is enough to register.
|
||||
_FrameVariable(self.py_db, frame, self._register_variable) # Instancing is enough to register.
|
||||
self._suspended_frames_manager._variable_reference_to_frames_tracker[frame_id] = self
|
||||
frame_ids_from_thread.append(frame_id)
|
||||
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ def get_clsname_for_code(code, frame):
|
|||
else: # instance method
|
||||
if hasattr(first_arg_obj, "__class__"):
|
||||
first_arg_class = first_arg_obj.__class__
|
||||
else: # old style class, fall back on type
|
||||
else: # old style class, fall back on type
|
||||
first_arg_class = type(first_arg_obj)
|
||||
func_name = code.co_name
|
||||
if hasattr(first_arg_class, func_name):
|
||||
|
|
@ -299,3 +299,71 @@ def isinstance_checked(obj, cls):
|
|||
except:
|
||||
return False
|
||||
|
||||
|
||||
class ScopeRequest(object):
|
||||
|
||||
__slots__ = ['variable_reference', 'scope']
|
||||
|
||||
def __init__(self, variable_reference, scope):
|
||||
assert scope in ('globals', 'locals')
|
||||
self.variable_reference = variable_reference
|
||||
self.scope = scope
|
||||
|
||||
def __eq__(self, o):
|
||||
if isinstance(o, ScopeRequest):
|
||||
return self.variable_reference == o.variable_reference and self.scope == o.scope
|
||||
|
||||
return False
|
||||
|
||||
def __ne__(self, o):
|
||||
return not self == o
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.variable_reference, self.scope))
|
||||
|
||||
|
||||
class DAPGrouper(object):
|
||||
'''
|
||||
Note: this is a helper class to group variables on the debug adapter protocol (DAP). For
|
||||
the xml protocol the type is just added to each variable and the UI can group/hide it as needed.
|
||||
'''
|
||||
|
||||
SCOPE_SPECIAL_VARS = 'special variables'
|
||||
SCOPE_PROTECTED_VARS = 'protected variables'
|
||||
SCOPE_FUNCTION_VARS = 'function variables'
|
||||
SCOPE_CLASS_VARS = 'class variables'
|
||||
|
||||
SCOPES_SORTED = [
|
||||
SCOPE_SPECIAL_VARS,
|
||||
SCOPE_PROTECTED_VARS,
|
||||
SCOPE_FUNCTION_VARS,
|
||||
SCOPE_CLASS_VARS,
|
||||
]
|
||||
|
||||
__slots__ = ['variable_reference', 'scope', 'contents_debug_adapter_protocol']
|
||||
|
||||
def __init__(self, scope):
|
||||
self.variable_reference = id(self)
|
||||
self.scope = scope
|
||||
self.contents_debug_adapter_protocol = []
|
||||
|
||||
def get_contents_debug_adapter_protocol(self):
|
||||
return self.contents_debug_adapter_protocol[:]
|
||||
|
||||
def __eq__(self, o):
|
||||
if isinstance(o, ScopeRequest):
|
||||
return self.variable_reference == o.variable_reference and self.scope == o.scope
|
||||
|
||||
return False
|
||||
|
||||
def __ne__(self, o):
|
||||
return not self == o
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.variable_reference, self.scope))
|
||||
|
||||
def __repr__(self):
|
||||
return ''
|
||||
|
||||
def __str__(self):
|
||||
return ''
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ def getVariable(dbg, thread_id, frame_id, scope, attrs):
|
|||
if attrs is not None:
|
||||
attrList = attrs.split('\t')
|
||||
for k in attrList:
|
||||
_type, _typeName, resolver = get_type(var)
|
||||
_type, _type_name, resolver = get_type(var)
|
||||
var = resolver.resolve(var, k)
|
||||
|
||||
return var
|
||||
|
|
@ -103,7 +103,7 @@ def getVariable(dbg, thread_id, frame_id, scope, attrs):
|
|||
# An Expression can be in any scope (globals/locals), therefore it needs to evaluated as an expression
|
||||
var = evaluate_expression(dbg, frame, attrList[count], False)
|
||||
else:
|
||||
_type, _typeName, resolver = get_type(var)
|
||||
_type, _type_name, resolver = get_type(var)
|
||||
var = resolver.resolve(var, attrList[count])
|
||||
else:
|
||||
if scope == "GLOBAL":
|
||||
|
|
@ -116,7 +116,7 @@ def getVariable(dbg, thread_id, frame_id, scope, attrs):
|
|||
var.update(frame.f_locals)
|
||||
|
||||
for k in attrList:
|
||||
_type, _typeName, resolver = get_type(var)
|
||||
_type, _type_name, resolver = get_type(var)
|
||||
var = resolver.resolve(var, k)
|
||||
|
||||
return var
|
||||
|
|
@ -137,8 +137,8 @@ def resolve_compound_variable_fields(dbg, thread_id, frame_id, scope, attrs):
|
|||
var = getVariable(dbg, thread_id, frame_id, scope, attrs)
|
||||
|
||||
try:
|
||||
_type, _typeName, resolver = get_type(var)
|
||||
return _typeName, resolver.get_dictionary(var)
|
||||
_type, type_name, resolver = get_type(var)
|
||||
return type_name, resolver.get_dictionary(var)
|
||||
except:
|
||||
pydev_log.exception('Error evaluating: thread_id: %s\nframe_id: %s\nscope: %s\nattrs: %s.',
|
||||
thread_id, frame_id, scope, attrs)
|
||||
|
|
@ -157,7 +157,7 @@ def resolve_var_object(var, attrs):
|
|||
else:
|
||||
attr_list = []
|
||||
for k in attr_list:
|
||||
type, _typeName, resolver = get_type(var)
|
||||
type, _type_name, resolver = get_type(var)
|
||||
var = resolver.resolve(var, k)
|
||||
return var
|
||||
|
||||
|
|
@ -173,11 +173,11 @@ def resolve_compound_var_object_fields(var, attrs):
|
|||
attr_list = attrs.split('\t')
|
||||
|
||||
for k in attr_list:
|
||||
type, _typeName, resolver = get_type(var)
|
||||
type, _type_name, resolver = get_type(var)
|
||||
var = resolver.resolve(var, k)
|
||||
|
||||
try:
|
||||
type, _typeName, resolver = get_type(var)
|
||||
type, _type_name, resolver = get_type(var)
|
||||
return resolver.get_dictionary(var)
|
||||
except:
|
||||
pydev_log.exception()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ from _pydevd_bundle.pydevd_constants import dict_iter_items, dict_keys, IS_PY3K,
|
|||
DEFAULT_VALUE
|
||||
from _pydev_bundle.pydev_imports import quote
|
||||
from _pydevd_bundle.pydevd_extension_api import TypeResolveProvider, StrPresentationProvider
|
||||
from _pydevd_bundle.pydevd_utils import isinstance_checked, hasattr_checked
|
||||
from _pydevd_bundle.pydevd_utils import isinstance_checked, hasattr_checked, DAPGrouper
|
||||
from _pydevd_bundle.pydevd_resolver import get_var_scope
|
||||
|
||||
try:
|
||||
import types
|
||||
|
|
@ -62,6 +63,8 @@ def _create_default_type_map():
|
|||
except:
|
||||
pass # not available on all python versions
|
||||
|
||||
default_type_map.append((DAPGrouper, pydevd_resolver.dapGrouperResolver))
|
||||
|
||||
try:
|
||||
default_type_map.append((set, pydevd_resolver.setResolver))
|
||||
except:
|
||||
|
|
@ -213,13 +216,13 @@ _TYPE_RESOLVE_HANDLER = TypeResolveHandler()
|
|||
|
||||
"""
|
||||
def get_type(o):
|
||||
Receives object and returns a triple (typeObject, typeString, resolver).
|
||||
Receives object and returns a triple (type_object, type_string, resolver).
|
||||
|
||||
resolver != None means that variable is a container, and should be displayed as a hierarchy.
|
||||
|
||||
Use the resolver to get its attributes.
|
||||
|
||||
All container objects should have a resolver.
|
||||
All container objects (i.e.: dict, list, tuple, object, etc) should have a resolver.
|
||||
"""
|
||||
get_type = _TYPE_RESOLVE_HANDLER.get_type
|
||||
|
||||
|
|
@ -260,6 +263,9 @@ def frame_vars_to_xml(frame_f_locals, hidden_ns=None):
|
|||
v = frame_f_locals[k]
|
||||
eval_full_val = should_evaluate_full_value(v)
|
||||
|
||||
if k == '_pydev_stop_at_break':
|
||||
continue
|
||||
|
||||
if k == RETURN_VALUES_DICT:
|
||||
for name, val in dict_iter_items(v):
|
||||
return_values_xml += var_to_xml(val, name, additional_in_xml=' isRetVal="True"')
|
||||
|
|
@ -354,6 +360,7 @@ def var_to_xml(val, name, trim_if_too_big=True, additional_in_xml='', evaluate_f
|
|||
type_name, type_qualifier, is_exception_on_eval, resolver, value = get_variable_details(
|
||||
val, evaluate_full_value)
|
||||
|
||||
scope = get_var_scope(name, val, '', True)
|
||||
try:
|
||||
name = quote(name, '/>_= ') # TODO: Fix PY-5834 without using quote
|
||||
except:
|
||||
|
|
@ -384,4 +391,7 @@ def var_to_xml(val, name, trim_if_too_big=True, additional_in_xml='', evaluate_f
|
|||
else:
|
||||
xml_container = ''
|
||||
|
||||
return ''.join((xml, xml_qualifier, xml_value, xml_container, additional_in_xml, ' />\n'))
|
||||
if scope:
|
||||
return ''.join((xml, xml_qualifier, xml_value, xml_container, additional_in_xml, ' scope="', scope, '"', ' />\n'))
|
||||
else:
|
||||
return ''.join((xml, xml_qualifier, xml_value, xml_container, additional_in_xml, ' />\n'))
|
||||
|
|
|
|||
|
|
@ -481,6 +481,8 @@ class PyDB(object):
|
|||
# Set communication protocol
|
||||
PyDevdAPI().set_protocol(self, 0, PydevdCustomization.DEFAULT_PROTOCOL)
|
||||
|
||||
self.variable_presentation = PyDevdAPI.VariablePresentation()
|
||||
|
||||
# mtime to be raised when breakpoints change
|
||||
self.mtime = 0
|
||||
|
||||
|
|
|
|||
|
|
@ -511,6 +511,15 @@ class DebuggerRunner(object):
|
|||
yield dct_with_stdout_stder
|
||||
except:
|
||||
fail_with_message = True
|
||||
# Let's print the actuayl exception here (it doesn't appear properly on Python 2 and
|
||||
# on Python 3 it's hard to find because pytest output is too verbose).
|
||||
sys.stderr.write('***********\n')
|
||||
sys.stderr.write('***********\n')
|
||||
sys.stderr.write('***********\n')
|
||||
traceback.print_exc()
|
||||
sys.stderr.write('***********\n')
|
||||
sys.stderr.write('***********\n')
|
||||
sys.stderr.write('***********\n')
|
||||
raise
|
||||
|
||||
if not writer.finished_ok:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
in_global_scope = 'in_global_scope_value'
|
||||
|
||||
|
||||
class SomeClass(object):
|
||||
|
||||
def method(self):
|
||||
print('breakpoint here')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
SomeClass().method()
|
||||
print('TEST SUCEEDED')
|
||||
|
|
@ -707,7 +707,7 @@ def test_case_15(case_setup):
|
|||
writer.finished_ok = True
|
||||
|
||||
|
||||
def test_case_16(case_setup):
|
||||
def test_case_16_resolve_numpy_array(case_setup):
|
||||
# numpy.ndarray resolver
|
||||
try:
|
||||
import numpy
|
||||
|
|
|
|||
|
|
@ -17,12 +17,13 @@ from _pydevd_bundle._debug_adapter.pydevd_schema import (ThreadEvent, ModuleEven
|
|||
from _pydevd_bundle.pydevd_comm_constants import file_system_encoding
|
||||
from _pydevd_bundle.pydevd_constants import (int_types, IS_64BIT_PROCESS,
|
||||
PY_VERSION_STR, PY_IMPL_VERSION_STR, PY_IMPL_NAME, IS_PY36_OR_GREATER, IS_PY39_OR_GREATER,
|
||||
IS_PY37_OR_GREATER)
|
||||
IS_PY37_OR_GREATER, IS_PYPY)
|
||||
from tests_python import debugger_unittest
|
||||
from tests_python.debug_constants import TEST_CHERRYPY, IS_PY2, TEST_DJANGO, TEST_FLASK, IS_PY26, \
|
||||
IS_PY27, IS_CPYTHON, TEST_GEVENT
|
||||
from tests_python.debugger_unittest import (IS_JYTHON, IS_APPVEYOR, overrides,
|
||||
get_free_port, wait_for_condition)
|
||||
from _pydevd_bundle.pydevd_utils import DAPGrouper
|
||||
|
||||
pytest_plugins = [
|
||||
str('tests_python.debugger_fixtures'),
|
||||
|
|
@ -108,7 +109,7 @@ class JsonFacade(object):
|
|||
|
||||
def write_make_initial_run(self):
|
||||
if not self._sent_launch_or_attach:
|
||||
self.write_launch()
|
||||
self._auto_write_launch()
|
||||
|
||||
configuration_done_request = self.write_request(pydevd_schema.ConfigurationDoneRequest())
|
||||
return self.wait_for_response(configuration_done_request)
|
||||
|
|
@ -160,7 +161,7 @@ class JsonFacade(object):
|
|||
Adds a breakpoint.
|
||||
'''
|
||||
if send_launch_if_needed and not self._sent_launch_or_attach:
|
||||
self.write_launch()
|
||||
self._auto_write_launch()
|
||||
|
||||
if isinstance(lines, int):
|
||||
lines = [lines]
|
||||
|
|
@ -240,6 +241,12 @@ class JsonFacade(object):
|
|||
request = {'type': 'request', 'command': command, 'arguments': arguments, 'seq':-1}
|
||||
self.wait_for_response(self.write_request(request))
|
||||
|
||||
def _auto_write_launch(self):
|
||||
self.write_launch(variablePresentation={
|
||||
"all": "hide",
|
||||
"protected": "inline",
|
||||
})
|
||||
|
||||
def write_launch(self, **arguments):
|
||||
return self._write_launch_or_attach('launch', **arguments)
|
||||
|
||||
|
|
@ -357,8 +364,8 @@ class JsonFacade(object):
|
|||
scopes = scopes_response.body.scopes
|
||||
name_to_scopes = dict((scope['name'], pydevd_schema.Scope(**scope)) for scope in scopes)
|
||||
|
||||
assert len(scopes) == 1
|
||||
assert sorted(name_to_scopes.keys()) == ['Locals']
|
||||
assert len(scopes) == 2
|
||||
assert sorted(name_to_scopes.keys()) == ['Globals', 'Locals']
|
||||
assert not name_to_scopes['Locals'].expensive
|
||||
|
||||
return name_to_scopes
|
||||
|
|
@ -372,11 +379,21 @@ class JsonFacade(object):
|
|||
|
||||
return self.get_name_to_var(name_to_scope['Locals'].variablesReference)
|
||||
|
||||
def get_globals_name_to_var(self, frame_id):
|
||||
name_to_scope = self.get_name_to_scope(frame_id)
|
||||
|
||||
return self.get_name_to_var(name_to_scope['Globals'].variablesReference)
|
||||
|
||||
def get_local_var(self, frame_id, var_name):
|
||||
ret = self.get_locals_name_to_var(frame_id)[var_name]
|
||||
assert ret.name == var_name
|
||||
return ret
|
||||
|
||||
def get_global_var(self, frame_id, var_name):
|
||||
ret = self.get_globals_name_to_var(frame_id)[var_name]
|
||||
assert ret.name == var_name
|
||||
return ret
|
||||
|
||||
def get_var(self, variables_reference, var_name=None, index=None):
|
||||
if var_name is not None:
|
||||
return self.get_name_to_var(variables_reference)[var_name]
|
||||
|
|
@ -1205,7 +1222,7 @@ def test_dict_ordered(case_setup):
|
|||
# : :type variables_response: VariablesResponse
|
||||
|
||||
variables_response = json_facade.get_variables_response(ref)
|
||||
assert [(d['name'], d['value']) for d in variables_response.body.variables if not d['name'].startswith('_OrderedDict')] == [
|
||||
assert [(d['name'], d['value']) for d in variables_response.body.variables if (not d['name'].startswith('_OrderedDict')) and (d['name'] not in DAPGrouper.SCOPES_SORTED)] == [
|
||||
('4', "'first'"), ('3', "'second'"), ('2', "'last'"), ('__len__', '3')]
|
||||
|
||||
json_facade.write_continue()
|
||||
|
|
@ -1255,7 +1272,8 @@ def test_stack_and_variables_dict(case_setup):
|
|||
]
|
||||
|
||||
variables_response = json_facade.get_variables_response(dict_variable_reference)
|
||||
assert variables_response.body.variables == [
|
||||
check = [x for x in variables_response.body.variables if x['name'] not in DAPGrouper.SCOPES_SORTED]
|
||||
assert check == [
|
||||
{'name': "'a'", 'value': '30', 'type': 'int', 'evaluateName': "variable_for_test_3['a']", 'variablesReference': 0 },
|
||||
{'name': "'b'", 'value': '20', 'type': 'int', 'evaluateName': "variable_for_test_3['b']", 'variablesReference': 0},
|
||||
{'name': '__len__', 'value': '2', 'type': 'int', 'evaluateName': 'len(variable_for_test_3)', 'variablesReference': 0, 'presentationHint': {'attributes': ['readOnly']}}
|
||||
|
|
@ -1420,7 +1438,8 @@ def test_stack_and_variables_set_and_list(case_setup):
|
|||
with case_setup.test_file('_debugger_case_local_variables2.py') as writer:
|
||||
json_facade = JsonFacade(writer)
|
||||
|
||||
writer.write_add_breakpoint(writer.get_line_index_with_content('Break here'))
|
||||
json_facade.write_launch()
|
||||
json_facade.write_set_breakpoints(writer.get_line_index_with_content('Break here'))
|
||||
json_facade.write_make_initial_run()
|
||||
|
||||
json_hit = json_facade.wait_for_thread_stopped()
|
||||
|
|
@ -1438,7 +1457,13 @@ def test_stack_and_variables_set_and_list(case_setup):
|
|||
]
|
||||
|
||||
variables_response = json_facade.get_variables_response(variables_references[0])
|
||||
assert variables_response.body.variables == [{
|
||||
cleaned_vars = _clear_groups(variables_response.body.variables)
|
||||
if IS_PYPY:
|
||||
# Functions are not found in PyPy.
|
||||
assert cleaned_vars.groups_found == set([DAPGrouper.SCOPE_SPECIAL_VARS])
|
||||
else:
|
||||
assert cleaned_vars.groups_found == set([DAPGrouper.SCOPE_SPECIAL_VARS, DAPGrouper.SCOPE_FUNCTION_VARS])
|
||||
assert cleaned_vars.variables == [{
|
||||
u'name': u'0',
|
||||
u'type': u'str',
|
||||
u'value': u"'a'",
|
||||
|
|
@ -1467,12 +1492,29 @@ def test_stack_and_variables_set_and_list(case_setup):
|
|||
writer.finished_ok = True
|
||||
|
||||
|
||||
_CleanedVars = namedtuple('_CleanedVars', 'variables, groups_found')
|
||||
|
||||
|
||||
def _clear_groups(variables):
|
||||
groups_found = set()
|
||||
new_variables = []
|
||||
for v in variables:
|
||||
if v['name'] in DAPGrouper.SCOPES_SORTED:
|
||||
groups_found.add(v['name'])
|
||||
continue
|
||||
|
||||
else:
|
||||
new_variables.append(v)
|
||||
|
||||
return _CleanedVars(new_variables, groups_found)
|
||||
|
||||
|
||||
@pytest.mark.skipif(IS_JYTHON, reason='Putting unicode on frame vars does not work on Jython.')
|
||||
def test_evaluate_unicode(case_setup):
|
||||
with case_setup.test_file('_debugger_case_local_variables.py') as writer:
|
||||
json_facade = JsonFacade(writer)
|
||||
|
||||
writer.write_add_breakpoint(writer.get_line_index_with_content('Break 2 here'))
|
||||
json_facade.write_set_breakpoints(writer.get_line_index_with_content('Break 2 here'))
|
||||
json_facade.write_make_initial_run()
|
||||
|
||||
json_hit = json_facade.wait_for_thread_stopped()
|
||||
|
|
@ -3062,8 +3104,9 @@ def test_path_translation_and_source_reference(case_setup):
|
|||
assert source_reference == 0 # When it's translated the source reference must be == 0
|
||||
|
||||
stack_frame_not_path_translated = stack_trace_response_body.stackFrames[1]
|
||||
assert stack_frame_not_path_translated['name'].startswith(
|
||||
'tests_python.resource_path_translation.other.call_me_back1 :')
|
||||
if not stack_frame_not_path_translated['name'].startswith(
|
||||
'tests_python.resource_path_translation.other.call_me_back1 :'):
|
||||
raise AssertionError('Error. Found: >>%s<<.' % (stack_frame_not_path_translated['name'],))
|
||||
|
||||
assert stack_frame_not_path_translated['source']['path'].endswith('other.py')
|
||||
source_reference = stack_frame_not_path_translated['source']['sourceReference']
|
||||
|
|
@ -3241,10 +3284,16 @@ def test_case_django_no_attribute_exception_breakpoint(case_setup_django, jmc):
|
|||
|
||||
if jmc:
|
||||
writer.write_set_project_roots([debugger_unittest._get_debugger_test_file('my_code')])
|
||||
json_facade.write_launch(debugOptions=['Django'])
|
||||
json_facade.write_launch(debugOptions=['Django'], variablePresentation={
|
||||
"all": "hide",
|
||||
"protected": "inline",
|
||||
})
|
||||
json_facade.write_set_exception_breakpoints(['raised'])
|
||||
else:
|
||||
json_facade.write_launch(debugOptions=['DebugStdLib', 'Django'])
|
||||
json_facade.write_launch(debugOptions=['DebugStdLib', 'Django'], variablePresentation={
|
||||
"all": "hide",
|
||||
"protected": "inline",
|
||||
})
|
||||
# Don't set to all 'raised' because we'd stop on standard library exceptions here
|
||||
# (which is not something we want).
|
||||
json_facade.write_set_exception_breakpoints(exception_options=[
|
||||
|
|
@ -4074,6 +4123,63 @@ def test_send_json_message(case_setup):
|
|||
writer.finished_ok = True
|
||||
|
||||
|
||||
def test_global_scope(case_setup):
|
||||
with case_setup.test_file('_debugger_case_globals.py') as writer:
|
||||
json_facade = JsonFacade(writer)
|
||||
json_facade.write_set_breakpoints(writer.get_line_index_with_content('breakpoint here'))
|
||||
|
||||
json_facade.write_make_initial_run()
|
||||
json_hit = json_facade.wait_for_thread_stopped()
|
||||
|
||||
local_var = json_facade.get_global_var(json_hit.frame_id, 'in_global_scope')
|
||||
assert local_var.value == "'in_global_scope_value'"
|
||||
json_facade.write_continue()
|
||||
|
||||
writer.finished_ok = True
|
||||
|
||||
|
||||
def _check_inline_var_presentation(json_facade, json_hit, variables_response):
|
||||
var_names = [v['name'] for v in variables_response.body.variables]
|
||||
assert var_names[:3] == ['SomeClass', 'in_global_scope', '__builtins__']
|
||||
|
||||
|
||||
def _check_hide_var_presentation(json_facade, json_hit, variables_response):
|
||||
var_names = [v['name'] for v in variables_response.body.variables]
|
||||
assert var_names == ['in_global_scope']
|
||||
|
||||
|
||||
def _check_class_group_special_inline_presentation(json_facade, json_hit, variables_response):
|
||||
var_names = [v['name'] for v in variables_response.body.variables]
|
||||
assert var_names[:3] == ['class variables', 'in_global_scope', '__builtins__']
|
||||
|
||||
variables_response = json_facade.get_variables_response(variables_response.body.variables[0]['variablesReference'])
|
||||
var_names = [v['name'] for v in variables_response.body.variables]
|
||||
assert var_names == ['SomeClass']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('var_presentation, check_func', [
|
||||
({"all": "inline"}, _check_inline_var_presentation),
|
||||
({"all": "hide"}, _check_hide_var_presentation),
|
||||
({"class": "group", "special": "inline"}, _check_class_group_special_inline_presentation),
|
||||
])
|
||||
def test_variable_presentation(case_setup, var_presentation, check_func):
|
||||
with case_setup.test_file('_debugger_case_globals.py') as writer:
|
||||
json_facade = JsonFacade(writer)
|
||||
json_facade.write_launch(variablePresentation=var_presentation)
|
||||
json_facade.write_set_breakpoints(writer.get_line_index_with_content('breakpoint here'))
|
||||
|
||||
json_facade.write_make_initial_run()
|
||||
json_hit = json_facade.wait_for_thread_stopped()
|
||||
name_to_scope = json_facade.get_name_to_scope(json_hit.frame_id)
|
||||
|
||||
variables_response = json_facade.get_variables_response(name_to_scope['Globals'].variablesReference)
|
||||
check_func(json_facade, json_hit, variables_response)
|
||||
|
||||
json_facade.write_continue()
|
||||
|
||||
writer.finished_ok = True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main(['-k', 'test_case_skipping_filters', '-s'])
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ def test_dict_resolver():
|
|||
from _pydevd_bundle.pydevd_resolver import DictResolver
|
||||
dict_resolver = DictResolver()
|
||||
dct = {(1, 2): 2, u'22': 22}
|
||||
contents_debug_adapter_protocol = dict_resolver.get_contents_debug_adapter_protocol(dct)
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(dict_resolver.get_contents_debug_adapter_protocol(dct))
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
check_len_entry(len_entry, ('__len__', 2))
|
||||
if IS_PY36_OR_GREATER:
|
||||
|
|
@ -31,7 +31,8 @@ def test_dict_resolver_hex():
|
|||
from _pydevd_bundle.pydevd_resolver import DictResolver
|
||||
dict_resolver = DictResolver()
|
||||
dct = {(1, 10, 100): (10000, 100000, 100000)}
|
||||
contents_debug_adapter_protocol = dict_resolver.get_contents_debug_adapter_protocol(dct, fmt={'hex': True})
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(
|
||||
dict_resolver.get_contents_debug_adapter_protocol(dct, fmt={'hex': True}))
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
check_len_entry(len_entry, ('__len__', 1))
|
||||
assert contents_debug_adapter_protocol == [
|
||||
|
|
@ -49,10 +50,10 @@ def test_object_resolver_simple():
|
|||
self.b = 20
|
||||
|
||||
obj = MyObject()
|
||||
dictionary = default_resolver.get_dictionary(obj)
|
||||
dictionary = clear_contents_dictionary(default_resolver.get_dictionary(obj))
|
||||
assert dictionary == {'a': 10, 'b': 20}
|
||||
|
||||
contents_debug_adapter_protocol = default_resolver.get_contents_debug_adapter_protocol(obj)
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(default_resolver.get_contents_debug_adapter_protocol(obj))
|
||||
assert contents_debug_adapter_protocol == [('a', 10, '.a'), ('b', 20, '.b')]
|
||||
|
||||
|
||||
|
|
@ -115,14 +116,15 @@ def test_object_resolver__dict__non_strings():
|
|||
self.__dict__[(1, 2)] = (3, 4)
|
||||
|
||||
obj = MyObject()
|
||||
dictionary = default_resolver.get_dictionary(obj)
|
||||
dictionary = clear_contents_dictionary(default_resolver.get_dictionary(obj))
|
||||
if IS_PY2:
|
||||
assert 'attribute name must be string' in dictionary.pop('(1, 2)')
|
||||
assert dictionary == {}
|
||||
else:
|
||||
assert dictionary == {'(1, 2)': (3, 4)}
|
||||
|
||||
contents_debug_adapter_protocol = default_resolver.get_contents_debug_adapter_protocol(obj)
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(
|
||||
default_resolver.get_contents_debug_adapter_protocol(obj))
|
||||
if IS_PY2:
|
||||
assert len(contents_debug_adapter_protocol) == 1
|
||||
entry = contents_debug_adapter_protocol[0]
|
||||
|
|
@ -145,7 +147,7 @@ def test_django_forms_resolver():
|
|||
|
||||
obj = MyObject()
|
||||
|
||||
dictionary = django_form_resolver.get_dictionary(obj)
|
||||
dictionary = clear_contents_dictionary(django_form_resolver.get_dictionary(obj))
|
||||
if IS_PY2:
|
||||
assert 'attribute name must be string' in dictionary.pop('(1, 2)')
|
||||
assert dictionary == {'errors': None}
|
||||
|
|
@ -153,7 +155,7 @@ def test_django_forms_resolver():
|
|||
assert dictionary == {'(1, 2)': (3, 4), 'errors': None}
|
||||
|
||||
obj._errors = 'bar'
|
||||
dictionary = django_form_resolver.get_dictionary(obj)
|
||||
dictionary = clear_contents_dictionary(django_form_resolver.get_dictionary(obj))
|
||||
if IS_PY2:
|
||||
assert 'attribute name must be string' in dictionary.pop('(1, 2)')
|
||||
assert dictionary == {'errors': 'bar', '_errors': 'bar'}
|
||||
|
|
@ -161,12 +163,42 @@ def test_django_forms_resolver():
|
|||
assert dictionary == {'(1, 2)': (3, 4), 'errors': 'bar', '_errors': 'bar'}
|
||||
|
||||
|
||||
def clear_contents_debug_adapter_protocol(contents_debug_adapter_protocol):
|
||||
lst = []
|
||||
for x in contents_debug_adapter_protocol:
|
||||
if x[0] == '__len__':
|
||||
if x[2] == '.__len__':
|
||||
# i.e.: remove a builtin __len__ method, but not the __len__ we add with the length.
|
||||
continue
|
||||
lst.append(x)
|
||||
|
||||
if not x[0].startswith('__'):
|
||||
|
||||
if '<built-in method' in str(x[1]) or '<method-wrapper' in str(x[1]) or '<bound method' in str(x[1]):
|
||||
continue
|
||||
|
||||
lst.append(x)
|
||||
|
||||
return lst
|
||||
|
||||
|
||||
def clear_contents_dictionary(dictionary):
|
||||
dictionary = dictionary.copy()
|
||||
for key in list(dictionary):
|
||||
if key == '__len__':
|
||||
continue
|
||||
if key.startswith('__') or key in ('count', 'index'):
|
||||
del dictionary[key]
|
||||
return dictionary
|
||||
|
||||
|
||||
def test_tuple_resolver():
|
||||
from _pydevd_bundle.pydevd_resolver import TupleResolver
|
||||
tuple_resolver = TupleResolver()
|
||||
fmt = {'hex': True}
|
||||
lst = tuple(range(11))
|
||||
contents_debug_adapter_protocol = tuple_resolver.get_contents_debug_adapter_protocol(lst)
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(
|
||||
tuple_resolver.get_contents_debug_adapter_protocol(lst))
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
assert contents_debug_adapter_protocol == [
|
||||
('00', 0, '[0]'),
|
||||
|
|
@ -183,7 +215,7 @@ def test_tuple_resolver():
|
|||
]
|
||||
check_len_entry(len_entry, ('__len__', 11))
|
||||
|
||||
assert tuple_resolver.get_dictionary(lst) == {
|
||||
assert clear_contents_dictionary(tuple_resolver.get_dictionary(lst)) == {
|
||||
'00': 0,
|
||||
'01': 1,
|
||||
'02': 2,
|
||||
|
|
@ -199,7 +231,8 @@ def test_tuple_resolver():
|
|||
}
|
||||
|
||||
lst = tuple(range(17))
|
||||
contents_debug_adapter_protocol = tuple_resolver.get_contents_debug_adapter_protocol(lst, fmt=fmt)
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(
|
||||
tuple_resolver.get_contents_debug_adapter_protocol(lst, fmt=fmt))
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
assert contents_debug_adapter_protocol == [
|
||||
('0x00', 0, '[0]'),
|
||||
|
|
@ -222,7 +255,7 @@ def test_tuple_resolver():
|
|||
]
|
||||
check_len_entry(len_entry, ('__len__', 17))
|
||||
|
||||
assert tuple_resolver.get_dictionary(lst, fmt=fmt) == {
|
||||
assert clear_contents_dictionary(tuple_resolver.get_dictionary(lst, fmt=fmt)) == {
|
||||
'0x00': 0,
|
||||
'0x01': 1,
|
||||
'0x02': 2,
|
||||
|
|
@ -244,7 +277,7 @@ def test_tuple_resolver():
|
|||
}
|
||||
|
||||
lst = tuple(range(10))
|
||||
contents_debug_adapter_protocol = tuple_resolver.get_contents_debug_adapter_protocol(lst)
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(tuple_resolver.get_contents_debug_adapter_protocol(lst))
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
assert contents_debug_adapter_protocol == [
|
||||
('0', 0, '[0]'),
|
||||
|
|
@ -260,7 +293,7 @@ def test_tuple_resolver():
|
|||
]
|
||||
check_len_entry(len_entry, ('__len__', 10))
|
||||
|
||||
assert tuple_resolver.get_dictionary(lst) == {
|
||||
assert clear_contents_dictionary(tuple_resolver.get_dictionary(lst)) == {
|
||||
'0': 0,
|
||||
'1': 1,
|
||||
'2': 2,
|
||||
|
|
@ -274,7 +307,7 @@ def test_tuple_resolver():
|
|||
'__len__': 10
|
||||
}
|
||||
|
||||
contents_debug_adapter_protocol = tuple_resolver.get_contents_debug_adapter_protocol(lst, fmt=fmt)
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(tuple_resolver.get_contents_debug_adapter_protocol(lst, fmt=fmt))
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
assert contents_debug_adapter_protocol == [
|
||||
('0x0', 0, '[0]'),
|
||||
|
|
@ -290,7 +323,7 @@ def test_tuple_resolver():
|
|||
]
|
||||
check_len_entry(len_entry, ('__len__', 10))
|
||||
|
||||
assert tuple_resolver.get_dictionary(lst, fmt=fmt) == {
|
||||
assert clear_contents_dictionary(tuple_resolver.get_dictionary(lst, fmt=fmt)) == {
|
||||
'0x0': 0,
|
||||
'0x1': 1,
|
||||
'0x2': 2,
|
||||
|
|
@ -314,7 +347,7 @@ def test_tuple_resolver_mixed():
|
|||
|
||||
my_tuple = CustomTuple([1, 2])
|
||||
my_tuple.some_value = 10
|
||||
contents_debug_adapter_protocol = tuple_resolver.get_contents_debug_adapter_protocol(my_tuple)
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(tuple_resolver.get_contents_debug_adapter_protocol(my_tuple))
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
check_len_entry(len_entry, ('__len__', 2))
|
||||
assert contents_debug_adapter_protocol == [
|
||||
|
|
|
|||
|
|
@ -15,10 +15,18 @@ def check_vars_dict_expected(as_dict, expected):
|
|||
assert as_dict == expected
|
||||
|
||||
|
||||
class _DummyPyDB(object):
|
||||
|
||||
def __init__(self):
|
||||
from _pydevd_bundle.pydevd_api import PyDevdAPI
|
||||
self.variable_presentation = PyDevdAPI.VariablePresentation()
|
||||
|
||||
|
||||
def test_suspended_frames_manager():
|
||||
from _pydevd_bundle.pydevd_suspended_frames import SuspendedFramesManager
|
||||
from _pydevd_bundle.pydevd_utils import DAPGrouper
|
||||
suspended_frames_manager = SuspendedFramesManager()
|
||||
py_db = None
|
||||
py_db = _DummyPyDB()
|
||||
with suspended_frames_manager.track_frames(py_db) as tracker:
|
||||
# : :type tracker: _FramesTracker
|
||||
thread_id = 'thread1'
|
||||
|
|
@ -57,7 +65,7 @@ def test_suspended_frames_manager():
|
|||
|
||||
var2 = dict((x.get_name(), x) for x in variable.get_children_variables())['var2']
|
||||
children_vars = var2.get_children_variables()
|
||||
as_dict = (dict([x.get_name(), x.get_var_data()] for x in children_vars))
|
||||
as_dict = (dict([x.get_name(), x.get_var_data()] for x in children_vars if x.get_name() not in DAPGrouper.SCOPES_SORTED))
|
||||
assert as_dict == {
|
||||
'0': {'name': '0', 'value': '1', 'type': 'int', 'evaluateName': 'var2[0]', 'variablesReference': 0 },
|
||||
'__len__': {'name': '__len__', 'value': '1', 'type': 'int', 'evaluateName': 'len(var2)', 'variablesReference': 0, 'presentationHint': {'attributes': ['readOnly']}, },
|
||||
|
|
@ -65,7 +73,7 @@ def test_suspended_frames_manager():
|
|||
|
||||
var3 = dict((x.get_name(), x) for x in variable.get_children_variables())['var3']
|
||||
children_vars = var3.get_children_variables()
|
||||
as_dict = (dict([x.get_name(), x.get_var_data()] for x in children_vars))
|
||||
as_dict = (dict([x.get_name(), x.get_var_data()] for x in children_vars if x.get_name() not in DAPGrouper.SCOPES_SORTED))
|
||||
assert isinstance(as_dict['33'].pop('variablesReference'), int_types) # The variable reference is always a new int.
|
||||
|
||||
check_vars_dict_expected(as_dict, {
|
||||
|
|
@ -99,7 +107,7 @@ def get_tuple_large_frame():
|
|||
def test_get_child_variables():
|
||||
from _pydevd_bundle.pydevd_suspended_frames import SuspendedFramesManager
|
||||
suspended_frames_manager = SuspendedFramesManager()
|
||||
py_db = None
|
||||
py_db = _DummyPyDB()
|
||||
for frame in (
|
||||
get_dict_large_frame(),
|
||||
get_set_large_frame(),
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ class DebugConfig(collections.MutableMapping):
|
|||
"connect": (),
|
||||
# Attach by PID
|
||||
"processId": (),
|
||||
"variablePresentation": {},
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ def test_variables(pyfile, target, run):
|
|||
print([a, b, c]) # @bp
|
||||
|
||||
with debug.Session() as session:
|
||||
session.config["variablePresentation"] = {"all": "hide", "protected": "inline"}
|
||||
with run(session, target(code_to_debug)):
|
||||
session.set_breakpoints(code_to_debug, all)
|
||||
|
||||
|
|
@ -134,6 +135,10 @@ def test_variable_sort(pyfile, target, run):
|
|||
print("done") # @bp
|
||||
|
||||
with debug.Session() as session:
|
||||
session.config["variablePresentation"] = {
|
||||
"special": "group",
|
||||
"protected": "inline",
|
||||
}
|
||||
with run(session, target(code_to_debug)):
|
||||
session.set_breakpoints(code_to_debug, all)
|
||||
|
||||
|
|
@ -155,11 +160,18 @@ def test_variable_sort(pyfile, target, run):
|
|||
"__a_test",
|
||||
"__b_test",
|
||||
"__c_test",
|
||||
"__a_test__",
|
||||
"__b_test__",
|
||||
"__c_test__",
|
||||
]
|
||||
|
||||
special_vars_entry = [v for v in vars if v["name"] == "special variables"][0]
|
||||
special_vars_variables = session.request(
|
||||
"variables",
|
||||
{"variablesReference": special_vars_entry["variablesReference"]},
|
||||
)["variables"]
|
||||
special_vars_variables = [
|
||||
v["name"] for v in special_vars_variables if "_test" in v["name"]
|
||||
]
|
||||
assert special_vars_variables == ["__a_test__", "__b_test__", "__c_test__"]
|
||||
|
||||
# String dict keys must be sorted as strings.
|
||||
b_test, = (v for v in vars if v["name"] == "b_test")
|
||||
b_test_vars = session.request(
|
||||
|
|
@ -167,9 +179,26 @@ def test_variable_sort(pyfile, target, run):
|
|||
)["variables"]
|
||||
var_names = [v["name"] for v in b_test_vars]
|
||||
if sys.version_info[:2] >= (3, 6):
|
||||
assert var_names == ["'spam'", "'eggs'", "'abcd'", "__len__"]
|
||||
# Note that the special __len__ we manually create is not added to special variables.
|
||||
expected = [
|
||||
"special variables",
|
||||
"function variables",
|
||||
"'spam'",
|
||||
"'eggs'",
|
||||
"'abcd'",
|
||||
"__len__",
|
||||
]
|
||||
else:
|
||||
assert var_names == ["'abcd'", "'eggs'", "'spam'", "__len__"]
|
||||
expected = [
|
||||
"special variables",
|
||||
"function variables",
|
||||
"'abcd'",
|
||||
"'eggs'",
|
||||
"'spam'",
|
||||
"__len__",
|
||||
]
|
||||
|
||||
assert var_names == expected
|
||||
|
||||
# Numeric dict keys must be sorted as numbers.
|
||||
if not "https://github.com/microsoft/ptvsd/issues/213":
|
||||
|
|
@ -178,7 +207,10 @@ def test_variable_sort(pyfile, target, run):
|
|||
"variables", {"variablesReference": c_test["variablesReference"]}
|
||||
)["variables"]
|
||||
var_names = [v["name"] for v in c_test_vars]
|
||||
assert var_names == ["1", "2", "10", "__len__"]
|
||||
# Note that the special __len__ we manually create is not added to special variables.
|
||||
expected = ["1", "2", "10", "__len__"]
|
||||
|
||||
assert var_names == expected
|
||||
|
||||
session.request_continue()
|
||||
|
||||
|
|
@ -301,6 +333,8 @@ def test_hex_numbers(pyfile, target, run):
|
|||
print((a, b, c, d)) # @bp
|
||||
|
||||
with debug.Session() as session:
|
||||
session.config["variablePresentation"] = {"all": "hide", "protected": "inline"}
|
||||
|
||||
with run(session, target(code_to_debug)):
|
||||
session.set_breakpoints(code_to_debug, all)
|
||||
|
||||
|
|
@ -480,7 +514,6 @@ def test_hex_numbers(pyfile, target, run):
|
|||
),
|
||||
]
|
||||
|
||||
|
||||
d_vars = session.request(
|
||||
"variables",
|
||||
{"variablesReference": d["variablesReference"], "format": {"hex": True}},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue