Repository: microsoft/ptvsd

Issue:
evaluateName of __len__ should be `len(x)` instead of `x.__len__()`.



Discussion:

Resolving patch:
diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_resolver.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_resolver.py
--- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_resolver.py
+++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_resolver.py
@@ -5,7 +5,7 @@
 import traceback
 from os.path import basename
 
-from _pydevd_bundle import pydevd_constants
+from functools import partial
 from _pydevd_bundle.pydevd_constants import dict_iter_items, dict_keys, xrange
 
 # Note: 300 is already a lot to see in the outline (after that the user should really use the shell to get things)
@@ -261,7 +261,7 @@ def get_contents_debug_adapter_protocol(self, dct):
                 ret.append((TOO_LARGE_ATTR, TOO_LARGE_MSG))
                 break
 
-        ret.append(('__len__', len(dct), '.__len__()'))
+        ret.append(('__len__', len(dct), partial(_apply_evaluate_name, evaluate_name='len(%s)')))
         # in case the class extends built-in type and has some additional fields
         from_default_resolver = defaultResolver.get_contents_debug_adapter_protocol(dct)
 
@@ -290,6 +290,10 @@ def get_dictionary(self, dict):
         return ret
 
 
+def _apply_evaluate_name(parent_name, evaluate_name):
+    return evaluate_name % (parent_name,)
+
+
 #=======================================================================================================================
 # TupleResolver
 #=======================================================================================================================
@@ -329,7 +333,7 @@ def get_contents_debug_adapter_protocol(self, lst):
                 ret.append((TOO_LARGE_ATTR, TOO_LARGE_MSG, None))
                 break
 
-        ret.append(('__len__', len(lst), '.__len__()'))
+        ret.append(('__len__', len(lst), partial(_apply_evaluate_name, evaluate_name='len(%s)')))
         # Needed in case the class extends the built-in type and has some additional fields.
         from_default_resolver = defaultResolver.get_dictionary(lst)
         if from_default_resolver:
diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_suspended_frames.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_suspended_frames.py
--- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_suspended_frames.py
+++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_suspended_frames.py
@@ -107,7 +107,10 @@ def get_children_variables(self, fmt=None):
             if parent_evaluate_name:
                 for key, val, evaluate_name in lst:
                     if evaluate_name is not None:
-                        evaluate_name = parent_evaluate_name + evaluate_name
+                        if callable(evaluate_name):
+                            evaluate_name = evaluate_name(parent_evaluate_name)
+                        else:
+                            evaluate_name = parent_evaluate_name + evaluate_name
                     variable = _ObjectVariable(
                         key, val, self._register_variable, evaluate_name=evaluate_name)
                     children_variables.append(variable)


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
@@ -458,7 +458,7 @@ def test_stack_and_variables_dict(case_setup):
         assert variables_response.body.variables == [
             {'name': "'a'", 'value': '30', 'type': 'int', 'evaluateName': "variable_for_test_3['a']" },
             {'name': "'b'", 'value': '20', 'type': 'int', 'evaluateName': "variable_for_test_3['b']"},
-            {'name': '__len__', 'value': '2', 'type': 'int', 'evaluateName': 'variable_for_test_3.__len__()'}
+            {'name': '__len__', 'value': '2', 'type': 'int', 'evaluateName': 'len(variable_for_test_3)'}
         ]
 
         writer.write_run_thread(hit.thread_id)
@@ -536,7 +536,7 @@ def test_stack_and_variables_set_and_list(case_setup):
             u'name': u'__len__',
             u'type': u'int',
             u'value': u'2',
-            u'evaluateName': u'variable_for_test_1.__len__()',
+            u'evaluateName': u'len(variable_for_test_1)',
         }]
 
         writer.write_run_thread(hit.thread_id)
diff --git a/src/ptvsd/_vendored/pydevd/tests_python/test_resolvers.py b/src/ptvsd/_vendored/pydevd/tests_python/test_resolvers.py
--- a/src/ptvsd/_vendored/pydevd/tests_python/test_resolvers.py
+++ b/src/ptvsd/_vendored/pydevd/tests_python/test_resolvers.py
@@ -1,17 +1,25 @@
 from tests_python.debug_constants import IS_PY2
 
 
+def check_len_entry(len_entry, first_2_params):
+    assert len_entry[:2] == first_2_params
+    assert callable(len_entry[2])
+    assert len_entry[2]('check') == 'len(check)'
+
+
 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)
+    len_entry = contents_debug_adapter_protocol.pop(-1)
+    check_len_entry(len_entry, ('__len__', 2))
     if IS_PY2:
         assert contents_debug_adapter_protocol == [
-            ('(1, 2)', 2, '[(1, 2)]'), (u"u'22'", 22, u"[u'22']"), ('__len__', 2, '.__len__()')]
+            ('(1, 2)', 2, '[(1, 2)]'), (u"u'22'", 22, u"[u'22']")]
     else:
         assert contents_debug_adapter_protocol == [
-            ("'22'", 22, "['22']"), ('(1, 2)', 2, '[(1, 2)]'), ('__len__', 2, '.__len__()')]
+            ("'22'", 22, "['22']"), ('(1, 2)', 2, '[(1, 2)]')]
 
 
 def test_tuple_resolver():
@@ -19,6 +27,7 @@ def test_tuple_resolver():
     tuple_resolver = TupleResolver()
     lst = tuple(range(11))
     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]'),
         ('01', 1, '[1]'),
@@ -31,8 +40,8 @@ def test_tuple_resolver():
         ('08', 8, '[8]'),
         ('09', 9, '[9]'),
         ('10', 10, '[10]'),
-        ('__len__', 11, '.__len__()')
     ]
+    check_len_entry(len_entry, ('__len__', 11))
 
     assert tuple_resolver.get_dictionary(lst) == {
         '00': 0,
@@ -51,6 +60,7 @@ def test_tuple_resolver():
 
     lst = tuple(range(10))
     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]'),
         ('1', 1, '[1]'),
@@ -62,8 +72,8 @@ def test_tuple_resolver():
         ('7', 7, '[7]'),
         ('8', 8, '[8]'),
         ('9', 9, '[9]'),
-        ('__len__', 10, '.__len__()')
     ]
+    check_len_entry(len_entry, ('__len__', 10))
 
     assert tuple_resolver.get_dictionary(lst) == {
         '0': 0,
diff --git a/src/ptvsd/_vendored/pydevd/tests_python/test_suspended_frames_manager.py b/src/ptvsd/_vendored/pydevd/tests_python/test_suspended_frames_manager.py
--- a/src/ptvsd/_vendored/pydevd/tests_python/test_suspended_frames_manager.py
+++ b/src/ptvsd/_vendored/pydevd/tests_python/test_suspended_frames_manager.py
@@ -58,7 +58,7 @@ def test_suspended_frames_manager():
         as_dict = (dict([x.get_name(), x.get_var_data()] for x in children_vars))
         assert as_dict == {
             '0': {'name': '0', 'value': '1', 'type': 'int', 'evaluateName': 'var2[0]' },
-            '__len__': {'name': '__len__', 'value': '1', 'type': 'int', 'evaluateName': 'var2.__len__()'},
+            '__len__': {'name': '__len__', 'value': '1', 'type': 'int', 'evaluateName': 'len(var2)'},
         }
 
         var3 = dict((x.get_name(), x) for x in variable.get_children_variables())['var3']
@@ -68,5 +68,5 @@ def test_suspended_frames_manager():
 
         check_vars_dict_expected(as_dict, {
             '33': {'name': '33', 'value': "[1]", 'type': 'list', 'evaluateName': 'var3[33]'},
-            '__len__': {'name': '__len__', 'value': '1', 'type': 'int', 'evaluateName': 'var3.__len__()'}
+            '__len__': {'name': '__len__', 'value': '1', 'type': 'int', 'evaluateName': 'len(var3)'}
         })
diff --git a/tests/func/test_evaluate.py b/tests/func/test_evaluate.py
--- a/tests/func/test_evaluate.py
+++ b/tests/func/test_evaluate.py
@@ -72,7 +72,7 @@ def code_to_debug():
             'type': 'int',
             'value': '2',
             'name': '__len__',
-            'evaluateName': "b.__len__()"
+            'evaluateName': "len(b)"
         }
 
         # simple variable