mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Add object id if variable name would be duplicate. Fixes #148
This commit is contained in:
parent
c265d74bc5
commit
cbcfe221d7
4 changed files with 145 additions and 3 deletions
|
|
@ -215,6 +215,30 @@ class DAPGrouperResolver:
|
|||
return obj.get_contents_debug_adapter_protocol()
|
||||
|
||||
|
||||
_basic_immutable_types = (int, float, complex, str, bytes, type(None), bool, frozenset)
|
||||
try:
|
||||
_basic_immutable_types += (long, unicode) # Py2 types
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
|
||||
def _does_obj_repr_evaluate_to_obj(obj):
|
||||
'''
|
||||
If obj is an object where evaluating its representation leads to
|
||||
the same object, return True, otherwise, return False.
|
||||
'''
|
||||
try:
|
||||
if isinstance(obj, tuple):
|
||||
for o in obj:
|
||||
if not _does_obj_repr_evaluate_to_obj(o):
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
return isinstance(obj, _basic_immutable_types)
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# DictResolver
|
||||
#=======================================================================================================================
|
||||
|
|
@ -267,11 +291,28 @@ class DictResolver:
|
|||
ret = []
|
||||
|
||||
i = 0
|
||||
|
||||
found_representations = set()
|
||||
|
||||
for key, val in dict_iter_items(dct):
|
||||
i += 1
|
||||
key_as_str = self.key_to_str(key, fmt)
|
||||
eval_key_str = self.key_to_str(key) # do not format the key
|
||||
ret.append((key_as_str, val, '[%s]' % (eval_key_str,)))
|
||||
|
||||
if key_as_str not in found_representations:
|
||||
found_representations.add(key_as_str)
|
||||
else:
|
||||
# If the key would be a duplicate, add the key id (otherwise
|
||||
# VSCode won't show all keys correctly).
|
||||
# See: https://github.com/microsoft/debugpy/issues/148
|
||||
key_as_str = '%s (id: %s)' % (key_as_str, id(key))
|
||||
found_representations.add(key_as_str)
|
||||
|
||||
if _does_obj_repr_evaluate_to_obj(key):
|
||||
s = self.key_to_str(key) # do not format the key
|
||||
eval_key_str = '[%s]' % (s,)
|
||||
else:
|
||||
eval_key_str = None
|
||||
ret.append((key_as_str, val, eval_key_str))
|
||||
if i > MAX_ITEMS_TO_HANDLE:
|
||||
ret.append((TOO_LARGE_ATTR, TOO_LARGE_MSG, None))
|
||||
break
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ def build_extension(dir_name, extension_name, target_pydevd_name, force_cython,
|
|||
Extension(
|
||||
"%s%s.%s" % (dir_name, "_ext" if extended else "", target_pydevd_name,),
|
||||
c_files,
|
||||
**kwargs,
|
||||
**kwargs
|
||||
)]
|
||||
|
||||
# This is needed in CPython 3.8 to be able to include internal/pycore_pystate.h
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
class T:
|
||||
|
||||
def __init__(self, name, value):
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
td = {T("foo", 24): "bar",
|
||||
T("gad", 42): "zooks",
|
||||
T("foo", 12): "bur"}
|
||||
|
||||
print('TEST SUCEEDED!') # Break here
|
||||
|
|
@ -1606,6 +1606,92 @@ def test_stack_and_variables_dict(case_setup):
|
|||
writer.finished_ok = True
|
||||
|
||||
|
||||
def test_variables_with_same_name(case_setup):
|
||||
with case_setup.test_file('_debugger_case_variables_with_same_name.py') as writer:
|
||||
json_facade = JsonFacade(writer)
|
||||
|
||||
writer.write_add_breakpoint(writer.get_line_index_with_content('Break here'))
|
||||
json_facade.write_make_initial_run()
|
||||
|
||||
json_hit = json_facade.wait_for_thread_stopped()
|
||||
json_hit = json_facade.get_stack_as_json_hit(json_hit.thread_id)
|
||||
|
||||
variables_response = json_facade.get_variables_response(json_hit.frame_id)
|
||||
|
||||
variables_references = json_facade.pop_variables_reference(variables_response.body.variables)
|
||||
dict_variable_reference = variables_references[0]
|
||||
assert isinstance(dict_variable_reference, int_types)
|
||||
# : :type variables_response: VariablesResponse
|
||||
|
||||
assert variables_response.body.variables == [
|
||||
{'name': 'td', 'value': "{foo: 'bar', gad: 'zooks', foo: 'bur'}", 'type': 'dict', 'evaluateName': 'td'}
|
||||
]
|
||||
|
||||
dict_variables_response = json_facade.get_variables_response(dict_variable_reference)
|
||||
# Note that we don't have the evaluateName because it's not possible to create a key
|
||||
# from the user object to actually get its value from the dict in this case.
|
||||
variables = dict_variables_response.body.variables[:]
|
||||
|
||||
found_foo = False
|
||||
found_foo_with_id = False
|
||||
for v in variables:
|
||||
if v['name'].startswith('foo'):
|
||||
if not found_foo:
|
||||
assert v['name'] == 'foo'
|
||||
found_foo = True
|
||||
else:
|
||||
assert v['name'].startswith('foo (id: ')
|
||||
v['name'] = 'foo'
|
||||
found_foo_with_id = True
|
||||
|
||||
assert found_foo
|
||||
assert found_foo_with_id
|
||||
|
||||
def compute_key(entry):
|
||||
return (entry['name'], entry['value'])
|
||||
|
||||
# Sort because the order may be different on Py2/Py3.
|
||||
assert sorted(variables, key=compute_key) == sorted([
|
||||
{
|
||||
'name': 'foo',
|
||||
'value': "'bar'",
|
||||
'type': 'str',
|
||||
'variablesReference': 0,
|
||||
'presentationHint': {'attributes': ['rawString']}
|
||||
},
|
||||
|
||||
{
|
||||
# 'name': 'foo (id: 2699272929584)', In the code above we changed this
|
||||
# to 'name': 'foo' for the comparisson.
|
||||
'name': 'foo',
|
||||
'value': "'bur'",
|
||||
'type': 'str',
|
||||
'variablesReference': 0,
|
||||
'presentationHint': {'attributes': ['rawString']}
|
||||
},
|
||||
|
||||
{
|
||||
'name': 'gad',
|
||||
'value': "'zooks'",
|
||||
'type': 'str',
|
||||
'variablesReference': 0,
|
||||
'presentationHint': {'attributes': ['rawString']}
|
||||
},
|
||||
|
||||
{
|
||||
'name': 'len()',
|
||||
'value': '3',
|
||||
'type': 'int',
|
||||
'evaluateName': 'len(td)',
|
||||
'variablesReference': 0,
|
||||
'presentationHint': {'attributes': ['readOnly']}
|
||||
},
|
||||
], key=compute_key)
|
||||
|
||||
json_facade.write_continue()
|
||||
writer.finished_ok = True
|
||||
|
||||
|
||||
def test_hasattr_failure(case_setup):
|
||||
with case_setup.test_file('_debugger_case_hasattr_crash.py') as writer:
|
||||
json_facade = JsonFacade(writer)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue