Repository: microsoft/ptvsd

Issue:
Debugging: Cannot inspect inside variables of section "Watch"
@DavidDuranPerez commented on [Thu Mar 28 2019](https://github.com/Microsoft/vscode-python/issues/4984)

## Environment data

- VS Code version: 1.32.3
- Extension version (available under the Extensions sidebar): 2019.3.6139
- OS and version: Windows 10
- Python version (& distribution if applicable, e.g. Anaconda): 3.6.2
- Type of virtual environment used (N/A | venv | virtualenv | conda | ...): virtualenv 

## Expected behaviour

I should be able to inspect inside any variable placed in the "Watch" section.

## Actual behaviour

Yesterday, I was able to inspect inside any variable placed in the "Watch" section. However, today, I can only inspect the automatic variables placed under "Locals". See image below:

![image](https://user-images.githubusercontent.com/25606109/55145050-4cd17e80-5142-11e9-907b-f86f1b663240.png)

This happens also to a colleague of mine since today. Is it because an update of the extension?



---

@0000Paula01 commented on [Thu Mar 28 2019](https://github.com/Microsoft/vscode-python/issues/4984#issuecomment-477518285)

I am also getting the same issue




Discussion:
@fabioz Looks like we are not providing `variableReference` correctly for evaluations.
what it should return:
`{"result": "[1, 2, 3]", "variablesReference": 6, "type": "list", "presentationHint": {}}`
actual:
`{"result": "[1, 2, 3]", "variablesReference": 0, "type": "list", "presentationHint": {}}`
I'll take a look at it.

Resolving patch:
diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py
--- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py
+++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py
@@ -918,7 +918,7 @@ def internal_evaluate_expression_json(py_db, request, thread_id):
 
     body = pydevd_schema.EvaluateResponseBody(
         result=var_data['value'],
-        variablesReference=var_data.get('variableReference', 0),
+        variablesReference=var_data.get('variablesReference', 0),
         type=var_data.get('type'),
         presentationHint=var_data.get('presentationHint'),
         namedVariables=var_data.get('namedVariables'),
@@ -1004,7 +1004,7 @@ def internal_set_expression_json(py_db, request, thread_id):
 
     body = pydevd_schema.SetExpressionResponseBody(
         value=var_data['value'],
-        variablesReference=var_data.get('variableReference', 0),
+        variablesReference=var_data.get('variablesReference', 0),
         type=var_data.get('type'),
         presentationHint=var_data.get('presentationHint'),
         namedVariables=var_data.get('namedVariables'),
@@ -1111,11 +1111,11 @@ def internal_get_exception_details_json(dbg, request, thread_id, set_additional_
                 if trace_obj is not None:
                     for frame_id, frame, method_name, filename_in_utf8, lineno in iter_visible_frames_info(
                             dbg, trace_obj.tb_frame, frame_id_to_lineno):
- 
+
                         line_text = linecache.getline(filename_in_utf8, lineno)
 
                         # Never filter out plugin frames!
-                        if not getattr(frame, 'IS_PLUGIN_FRAME', False):  
+                        if not getattr(frame, 'IS_PLUGIN_FRAME', False):
                             if not dbg.in_project_scope(filename_in_utf8):
                                 if not dbg.get_use_libraries_filter():
                                     continue
@@ -1132,7 +1132,7 @@ def internal_get_exception_details_json(dbg, request, thread_id, set_additional_
 
         stack_str = ''.join(traceback.format_list(frames))
 
-        # This is an extra bit of data used by Visual Studio 
+        # This is an extra bit of data used by Visual Studio
         source_path = frames[0][0] if frames else ''
 
         # TODO: breakMode is set to always. This should be retrieved from exception
diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py
--- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py
+++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py
@@ -80,6 +80,7 @@ def _convert_rules_to_exclude_filters(rules, filename_to_server, on_error):
 
 
 class IDMap(object):
+
     def __init__(self):
         self._value_to_key = {}
         self._key_to_value = {}
@@ -541,7 +542,7 @@ def on_gototargets_request(self, py_db, request):
         target_id = self._goto_targets_map.obtain_key((path, line))
         target = {
             'id': target_id,
-            'label': '{}:{}'.format(path, line),
+            'label': '%s:%s' % (path, line),
             'line': line
         }
         body = GotoTargetsResponseBody(targets=[target])
@@ -567,4 +568,5 @@ def on_goto_request(self, py_db, request):
         response = pydevd_base_schema.build_response(request, kwargs={'body': {}})
         return NetCommand(CMD_RETURN, 0, response, is_json=True)
 
+
 process_net_command_json = _PyDevJsonCommandProcessor(pydevd_base_schema.from_json).process_net_command_json
diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_xml.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_xml.py
--- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_xml.py
+++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_xml.py
@@ -32,77 +32,75 @@ def __init__(self, result):
 
 
 def _create_default_type_map():
-    if not _IS_JYTHON:
-        default_type_map = [
-            # None means that it should not be treated as a compound variable
-
-            # isintance does not accept a tuple on some versions of python, so, we must declare it expanded
-            (type(None), None,),
-            (int, None),
-            (float, None),
-            (complex, None),
-            (str, None),
-            (tuple, pydevd_resolver.tupleResolver),
-            (list, pydevd_resolver.tupleResolver),
-            (dict, pydevd_resolver.dictResolver),
-        ]
-        try:
-            default_type_map.append((long, None))  # @UndefinedVariable
-        except:
-            pass  # not available on all python versions
+    default_type_map = [
+        # None means that it should not be treated as a compound variable
+
+        # isintance does not accept a tuple on some versions of python, so, we must declare it expanded
+        (type(None), None,),
+        (int, None),
+        (float, None),
+        (complex, None),
+        (str, None),
+        (tuple, pydevd_resolver.tupleResolver),
+        (list, pydevd_resolver.tupleResolver),
+        (dict, pydevd_resolver.dictResolver),
+    ]
+    try:
+        default_type_map.append((long, None))  # @UndefinedVariable
+    except:
+        pass  # not available on all python versions
 
-        try:
-            default_type_map.append((unicode, None))  # @UndefinedVariable
-        except:
-            pass  # not available on all python versions
+    try:
+        default_type_map.append((unicode, None))  # @UndefinedVariable
+    except:
+        pass  # not available on all python versions
 
-        try:
-            default_type_map.append((set, pydevd_resolver.setResolver))
-        except:
-            pass  # not available on all python versions
+    try:
+        default_type_map.append((set, pydevd_resolver.setResolver))
+    except:
+        pass  # not available on all python versions
 
-        try:
-            default_type_map.append((frozenset, pydevd_resolver.setResolver))
-        except:
-            pass  # not available on all python versions
+    try:
+        default_type_map.append((frozenset, pydevd_resolver.setResolver))
+    except:
+        pass  # not available on all python versions
 
-        try:
-            from django.utils.datastructures import MultiValueDict
-            default_type_map.insert(0, (MultiValueDict, pydevd_resolver.multiValueDictResolver))
-            # we should put it before dict
-        except:
-            pass  # django may not be installed
+    try:
+        from django.utils.datastructures import MultiValueDict
+        default_type_map.insert(0, (MultiValueDict, pydevd_resolver.multiValueDictResolver))
+        # we should put it before dict
+    except:
+        pass  # django may not be installed
 
-        try:
-            from django.forms import BaseForm
-            default_type_map.insert(0, (BaseForm, pydevd_resolver.djangoFormResolver))
-            # we should put it before instance resolver
-        except:
-            pass  # django may not be installed
+    try:
+        from django.forms import BaseForm
+        default_type_map.insert(0, (BaseForm, pydevd_resolver.djangoFormResolver))
+        # we should put it before instance resolver
+    except:
+        pass  # django may not be installed
 
-        try:
-            from collections import deque
-            default_type_map.append((deque, pydevd_resolver.dequeResolver))
-        except:
-            pass
+    try:
+        from collections import deque
+        default_type_map.append((deque, pydevd_resolver.dequeResolver))
+    except:
+        pass
 
-        if frame_type is not None:
-            default_type_map.append((frame_type, pydevd_resolver.frameResolver))
+    if frame_type is not None:
+        default_type_map.append((frame_type, pydevd_resolver.frameResolver))
 
-    else:
+    if _IS_JYTHON:
         from org.python import core  # @UnresolvedImport
-        default_type_map = [
-            (core.PyNone, None),
-            (core.PyInteger, None),
-            (core.PyLong, None),
-            (core.PyFloat, None),
-            (core.PyComplex, None),
-            (core.PyString, None),
-            (core.PyTuple, pydevd_resolver.tupleResolver),
-            (core.PyList, pydevd_resolver.tupleResolver),
-            (core.PyDictionary, pydevd_resolver.dictResolver),
-            (core.PyStringMap, pydevd_resolver.dictResolver),
-        ]
+        default_type_map.append((core.PyNone, None))
+        default_type_map.append((core.PyInteger, None))
+        default_type_map.append((core.PyLong, None))
+        default_type_map.append((core.PyFloat, None))
+        default_type_map.append((core.PyComplex, None))
+        default_type_map.append((core.PyString, None))
+        default_type_map.append((core.PyTuple, pydevd_resolver.tupleResolver))
+        default_type_map.append((core.PyList, pydevd_resolver.tupleResolver))
+        default_type_map.append((core.PyDictionary, pydevd_resolver.dictResolver))
+        default_type_map.append((core.PyStringMap, pydevd_resolver.dictResolver))
+
         if hasattr(core, 'PyJavaInstance'):
             # Jython 2.5b3 removed it.
             default_type_map.append((core.PyJavaInstance, pydevd_resolver.instanceResolver))
@@ -181,7 +179,7 @@ def _get_type(self, o, type_object, type_name):
             if type_name == 'org.python.core.PyArray':
                 return type_object, type_name, pydevd_resolver.jyArrayResolver
 
-            return self._base_get_type(o, type_name, type_name)
+            return self._base_get_type(o, type_object, type_name)
 
     def str_from_providers(self, o, type_object, type_name):
         provider = self._type_to_str_provider_cache.get(type_object)


Test patch:
diff --git a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py
--- a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py
+++ b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py
@@ -575,15 +575,34 @@ def test_evaluate_unicode(case_setup):
         evaluate_response = json_facade.wait_for_response(
             json_facade.write_request(EvaluateRequest(EvaluateArguments(u'\u16A0', json_hit.frameId))))
 
+        evaluate_response_body = evaluate_response.body.to_dict()
+
         if IS_PY2:
-            assert evaluate_response.body.to_dict() == {
+            # The error can be referenced.
+            variables_reference = json_facade.pop_variables_reference([evaluate_response_body])
+
+            assert evaluate_response_body == {
                 'result': u"SyntaxError('invalid syntax', ('<string>', 1, 1, '\\xe1\\x9a\\xa0'))",
                 'type': u'SyntaxError',
-                'variablesReference': 0,
                 'presentationHint': {},
             }
+
+            assert len(variables_reference) == 1
+            reference = variables_reference[0]
+            assert reference > 0
+            variables_response = json_facade.get_variables_response(reference)
+            child_variables = variables_response.to_dict()['body']['variables']
+            assert len(child_variables) == 1
+            assert json_facade.pop_variables_reference(child_variables)[0] > 0
+            assert child_variables == [{
+                u'type': u'SyntaxError',
+                u'evaluateName': u'\u16a0.result',
+                u'name': u'result',
+                u'value': u"SyntaxError('invalid syntax', ('<string>', 1, 1, '\\xe1\\x9a\\xa0'))"
+            }]
+
         else:
-            assert evaluate_response.body.to_dict() == {
+            assert evaluate_response_body == {
                 'result': "'\u16a1'",
                 'type': 'str',
                 'variablesReference': 0,
@@ -594,6 +613,58 @@ def test_evaluate_unicode(case_setup):
         writer.finished_ok = True
 
 
+def test_evaluate_variable_references(case_setup):
+    from _pydevd_bundle._debug_adapter.pydevd_schema import EvaluateRequest
+    from _pydevd_bundle._debug_adapter.pydevd_schema import EvaluateArguments
+    with case_setup.test_file('_debugger_case_local_variables2.py') as writer:
+        json_facade = JsonFacade(writer)
+
+        writer.write_set_protocol('http_json')
+
+        writer.write_add_breakpoint(writer.get_line_index_with_content('Break here'))
+        json_facade.write_make_initial_run()
+
+        hit = writer.wait_for_breakpoint_hit()
+        json_hit = json_facade.get_stack_as_json_hit(hit.thread_id)
+
+        evaluate_response = json_facade.wait_for_response(
+            json_facade.write_request(EvaluateRequest(EvaluateArguments('variable_for_test_2', json_hit.frameId))))
+
+        evaluate_response_body = evaluate_response.body.to_dict()
+
+        variables_reference = json_facade.pop_variables_reference([evaluate_response_body])
+
+        assert evaluate_response_body == {
+            'type': 'set',
+            'result': "set(['a'])" if IS_PY2 else "{'a'}",
+            'presentationHint': {},
+        }
+        assert len(variables_reference) == 1
+        reference = variables_reference[0]
+        assert reference > 0
+        variables_response = json_facade.get_variables_response(reference)
+        child_variables = variables_response.to_dict()['body']['variables']
+
+        # The name for a reference in a set is the id() of the variable and can change at each run.
+        del child_variables[0]['name']
+
+        assert child_variables == [
+            {
+                'type': 'str',
+                'value': "'a'",
+                'presentationHint': {'attributes': ['rawString']},
+            },
+            {
+                'name': '__len__',
+                'type': 'int',
+                'value': '1'
+            }
+        ]
+
+        writer.write_run_thread(hit.thread_id)
+        writer.finished_ok = True
+
+
 def test_set_expression(case_setup):
     from _pydevd_bundle._debug_adapter.pydevd_schema import SetExpressionRequest
     from _pydevd_bundle._debug_adapter.pydevd_schema import SetExpressionArguments
@@ -1022,6 +1093,7 @@ def test_exception_details(case_setup):
         writer.finished_ok = True
 
 
+@pytest.mark.skipif(IS_JYTHON, reason='No goto on Jython.')
 def test_goto(case_setup):
     with case_setup.test_file('_debugger_case_set_next_statement.py') as writer:
         json_facade = JsonFacade(writer)