diff --git a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_resolver.py b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_resolver.py index de78ce82e..41e198ee9 100644 --- a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_resolver.py +++ b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_resolver.py @@ -8,7 +8,7 @@ from os.path import basename from functools import partial -from _pydevd_bundle.pydevd_constants import dict_iter_items, dict_keys, xrange +from _pydevd_bundle.pydevd_constants import dict_iter_items, dict_keys, xrange, IS_PY36_OR_GREATER 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) @@ -241,6 +241,8 @@ def _get_py_dictionary(self, var, names=None, used___dict__=False): #======================================================================================================================= class DictResolver: + sort_keys = not IS_PY36_OR_GREATER + def resolve(self, dict, key): if key in ('__len__', TOO_LARGE_ATTR): return None @@ -302,6 +304,9 @@ def get_contents_debug_adapter_protocol(self, dct, fmt=None): if from_default_resolver: ret = from_default_resolver + ret + if not self.sort_keys: + return ret + return sorted(ret, key=lambda tup: sorted_attributes_key(tup[0])) def get_dictionary(self, dict): @@ -580,6 +585,8 @@ def get_dictionary(self, var): #======================================================================================================================= class OrderedDictResolver(DictResolver): + sort_keys = False + def init_dict(self): return OrderedDict() diff --git a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_xml.py b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_xml.py index aaf42e8a9..86110317c 100644 --- a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_xml.py +++ b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_xml.py @@ -45,6 +45,13 @@ def _create_default_type_map(): (list, pydevd_resolver.tupleResolver), (dict, pydevd_resolver.dictResolver), ] + try: + from collections import OrderedDict + default_type_map.insert(0, (OrderedDict, pydevd_resolver.orderedDictResolver)) + # we should put it before dict + except: + pass + try: default_type_map.append((long, None)) # @UndefinedVariable except: diff --git a/src/debugpy/_vendored/pydevd/tests_python/resources/_debugger_case_odict.py b/src/debugpy/_vendored/pydevd/tests_python/resources/_debugger_case_odict.py new file mode 100644 index 000000000..dadbf026d --- /dev/null +++ b/src/debugpy/_vendored/pydevd/tests_python/resources/_debugger_case_odict.py @@ -0,0 +1,14 @@ +def check(): + from collections import OrderedDict + import sys + # On 3.6 onwards, use a regular dict. + odict = OrderedDict() if sys.version_info[:2] < (3, 6) else {} + odict[4] = 'first' + odict[3] = 'second' + odict[2] = 'last' + print('break here') + + +if __name__ == '__main__': + check() +print('TEST SUCEEDED') diff --git a/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py b/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py index 533a192fd..74cbaa767 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py +++ b/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py @@ -16,7 +16,8 @@ InitializeRequestArguments, TerminateArguments, TerminateRequest, TerminatedEvent) 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) + PY_VERSION_STR, PY_IMPL_VERSION_STR, PY_IMPL_NAME, IS_PY36_OR_GREATER, IS_PY39_OR_GREATER, + IS_PY37_OR_GREATER) 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 @@ -1177,6 +1178,38 @@ def test_modules(case_setup): writer.finished_ok = True +@pytest.mark.skipif(IS_PY26, reason='Python 2.6 does not have an ordered dict') +def test_dict_ordered(case_setup): + with case_setup.test_file('_debugger_case_odict.py') as writer: + json_facade = JsonFacade(writer) + + 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() + 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 = variables_response.body.variables + for dct in variables_references: + if dct['name'] == 'odict': + break + else: + raise AssertionError('Expected to find "odict".') + ref = dct['variablesReference'] + + assert isinstance(ref, int_types) + # : :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')] == [ + ('4', "'first'"), ('3', "'second'"), ('2', "'last'"), ('__len__', '3')] + + json_facade.write_continue() + writer.finished_ok = True + + @pytest.mark.skipif(IS_JYTHON, reason='Putting unicode on frame vars does not work on Jython.') def test_stack_and_variables_dict(case_setup): with case_setup.test_file('_debugger_case_local_variables.py') as writer: diff --git a/src/debugpy/_vendored/pydevd/tests_python/test_resolvers.py b/src/debugpy/_vendored/pydevd/tests_python/test_resolvers.py index 76b273f1e..90a74ec5f 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/test_resolvers.py +++ b/src/debugpy/_vendored/pydevd/tests_python/test_resolvers.py @@ -1,4 +1,5 @@ from tests_python.debug_constants import IS_PY2 +from _pydevd_bundle.pydevd_constants import IS_PY36_OR_GREATER def check_len_entry(len_entry, first_2_params): @@ -14,7 +15,11 @@ def test_dict_resolver(): 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: + if IS_PY36_OR_GREATER: + assert contents_debug_adapter_protocol == [ + ('(1, 2)', 2, '[(1, 2)]'), ("'22'", 22, "['22']")] + + elif IS_PY2: assert contents_debug_adapter_protocol == [ ('(1, 2)', 2, '[(1, 2)]'), (u"u'22'", 22, u"[u'22']")] else: