Add object id if variable name would be duplicate. Fixes #148

This commit is contained in:
Fabio Zadrozny 2021-01-22 15:24:55 -03:00
parent c265d74bc5
commit cbcfe221d7
4 changed files with 145 additions and 3 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)