mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
resolve generic collections
This commit is contained in:
parent
6cbdf8767e
commit
8fba4ebc2b
3 changed files with 83 additions and 124 deletions
|
|
@ -238,9 +238,9 @@ def _does_obj_repr_evaluate_to_obj(obj):
|
|||
|
||||
|
||||
# =======================================================================================================================
|
||||
# DictResolver
|
||||
# MappingResolver
|
||||
# =======================================================================================================================
|
||||
class DictResolver:
|
||||
class MappingResolver:
|
||||
sort_keys = not IS_PY36_OR_GREATER
|
||||
|
||||
def resolve(self, dct, key):
|
||||
|
|
@ -452,7 +452,7 @@ class ForwardInternalResolverToObject:
|
|||
return var.resolve(attribute)
|
||||
|
||||
|
||||
class TupleResolver: # to enumerate tuples and lists
|
||||
class SequenceResolver:
|
||||
def resolve(self, var, attribute):
|
||||
"""
|
||||
:param var: that's the original object we're dealing with.
|
||||
|
|
@ -657,7 +657,7 @@ class JyArrayResolver:
|
|||
# =======================================================================================================================
|
||||
# MultiValueDictResolver
|
||||
# =======================================================================================================================
|
||||
class MultiValueDictResolver(DictResolver):
|
||||
class MultiValueDictResolver(MappingResolver):
|
||||
def resolve(self, dct, key):
|
||||
if key in (GENERATED_LEN_ATTR_NAME, TOO_LARGE_ATTR):
|
||||
return None
|
||||
|
|
@ -699,9 +699,9 @@ class DjangoFormResolver(DefaultResolver):
|
|||
# =======================================================================================================================
|
||||
# DequeResolver
|
||||
# =======================================================================================================================
|
||||
class DequeResolver(TupleResolver):
|
||||
class DequeResolver(SequenceResolver):
|
||||
def get_dictionary(self, var):
|
||||
d = TupleResolver.get_dictionary(self, var)
|
||||
d = SequenceResolver.get_dictionary(self, var)
|
||||
d["maxlen"] = getattr(var, "maxlen", None)
|
||||
return d
|
||||
|
||||
|
|
@ -709,7 +709,7 @@ class DequeResolver(TupleResolver):
|
|||
# =======================================================================================================================
|
||||
# OrderedDictResolver
|
||||
# =======================================================================================================================
|
||||
class OrderedDictResolver(DictResolver):
|
||||
class OrderedDictResolver(MappingResolver):
|
||||
sort_keys = False
|
||||
|
||||
def init_dict(self):
|
||||
|
|
@ -765,8 +765,8 @@ class FrameResolver:
|
|||
|
||||
|
||||
defaultResolver = DefaultResolver()
|
||||
dictResolver = DictResolver()
|
||||
tupleResolver = TupleResolver()
|
||||
mappingResolver = MappingResolver()
|
||||
sequenceResolver = SequenceResolver()
|
||||
instanceResolver = InstanceResolver()
|
||||
jyArrayResolver = JyArrayResolver()
|
||||
setResolver = SetResolver()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
import sys
|
||||
|
||||
from collections import deque, OrderedDict
|
||||
from collections.abc import Mapping, Sequence, Set as AbstractSet
|
||||
from types import FrameType
|
||||
from typing import Optional
|
||||
|
||||
from _pydev_bundle import pydev_log
|
||||
from _pydevd_bundle import pydevd_extension_utils
|
||||
from _pydevd_bundle import pydevd_resolver
|
||||
import sys
|
||||
from _pydevd_bundle.pydevd_constants import (
|
||||
BUILTINS_MODULE_NAME,
|
||||
MAXIMUM_VARIABLE_REPRESENTATION_SIZE,
|
||||
|
|
@ -13,14 +19,6 @@ from _pydev_bundle.pydev_imports import quote
|
|||
from _pydevd_bundle.pydevd_extension_api import TypeResolveProvider, StrPresentationProvider
|
||||
from _pydevd_bundle.pydevd_utils import isinstance_checked, hasattr_checked, DAPGrouper
|
||||
from _pydevd_bundle.pydevd_resolver import get_var_scope, MoreItems, MoreItemsRange
|
||||
from typing import Optional
|
||||
|
||||
try:
|
||||
import types
|
||||
|
||||
frame_type = types.FrameType
|
||||
except:
|
||||
frame_type = None
|
||||
|
||||
|
||||
def make_valid_xml_value(s):
|
||||
|
|
@ -40,97 +38,47 @@ _IS_JYTHON = sys.platform.startswith("java")
|
|||
|
||||
def _create_default_type_map():
|
||||
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),
|
||||
# non-compound types
|
||||
((type(None), int, float, complex, str), None),
|
||||
# collections
|
||||
(Sequence, pydevd_resolver.sequenceResolver),
|
||||
(deque, pydevd_resolver.dequeResolver),
|
||||
(OrderedDict, pydevd_resolver.orderedDictResolver),
|
||||
(Mapping, pydevd_resolver.mappingResolver),
|
||||
(AbstractSet, pydevd_resolver.setResolver),
|
||||
# other builtin types
|
||||
(FrameType, pydevd_resolver.frameResolver),
|
||||
# pydevd types
|
||||
(DAPGrouper, pydevd_resolver.dapGrouperResolver),
|
||||
((MoreItems, MoreItemsRange), pydevd_resolver.forwardInternalResolverToObject),
|
||||
]
|
||||
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:
|
||||
pass # not available on all python versions
|
||||
|
||||
default_type_map.append((DAPGrouper, pydevd_resolver.dapGrouperResolver))
|
||||
default_type_map.append((MoreItems, pydevd_resolver.forwardInternalResolverToObject))
|
||||
default_type_map.append((MoreItemsRange, pydevd_resolver.forwardInternalResolverToObject))
|
||||
|
||||
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:
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
|
||||
from django.forms import BaseForm
|
||||
except ImportError:
|
||||
pass # django may not be installed
|
||||
else:
|
||||
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 collections import deque
|
||||
|
||||
default_type_map.append((deque, pydevd_resolver.dequeResolver))
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
from ctypes import Array
|
||||
|
||||
default_type_map.append((Array, pydevd_resolver.tupleResolver))
|
||||
except:
|
||||
pass
|
||||
|
||||
if frame_type is not None:
|
||||
default_type_map.append((frame_type, pydevd_resolver.frameResolver))
|
||||
except ImportError:
|
||||
pass # TODO: comment on reason why this might this not be available
|
||||
else:
|
||||
default_type_map.append((Array, pydevd_resolver.sequenceResolver))
|
||||
|
||||
if _IS_JYTHON:
|
||||
from org.python import core # @UnresolvedImport
|
||||
|
||||
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))
|
||||
default_type_map += [
|
||||
((core.PyNone, core.PyInteger, core.PyLong, core.PyFloat, core.PyComplex, core.PyString), None),
|
||||
((core.PyTuple, core.PyList), pydevd_resolver.sequenceResolver),
|
||||
((core.PyDictionary, core.PyStringMap), pydevd_resolver.mappingResolver),
|
||||
]
|
||||
|
||||
return default_type_map
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from types import MappingProxyType
|
||||
from typing import TYPE_CHECKING
|
||||
from collections import UserDict, UserList
|
||||
|
||||
import pytest
|
||||
from _pydevd_bundle.pydevd_constants import IS_PY36_OR_GREATER, GENERATED_LEN_ATTR_NAME
|
||||
from _pydevd_bundle import pydevd_constants, pydevd_frame_utils
|
||||
import pytest
|
||||
import sys
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Mapping, Sequence
|
||||
|
||||
|
||||
def check_len_entry(len_entry, first_2_params):
|
||||
|
|
@ -10,12 +19,13 @@ def check_len_entry(len_entry, first_2_params):
|
|||
assert len_entry[2]("check") == "len(check)"
|
||||
|
||||
|
||||
def test_dict_resolver():
|
||||
from _pydevd_bundle.pydevd_resolver import DictResolver
|
||||
@pytest.mark.parametrize("map_cls", [dict, MappingProxyType, UserDict])
|
||||
def test_mapping_resolver(map_cls: type[Mapping]):
|
||||
from _pydevd_bundle.pydevd_resolver import MappingResolver
|
||||
|
||||
dict_resolver = DictResolver()
|
||||
dct = {(1, 2): 2, "22": 22}
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(dict_resolver.get_contents_debug_adapter_protocol(dct))
|
||||
mapping_resolver = MappingResolver()
|
||||
dct = map_cls({(1, 2): 2, "22": 22})
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(mapping_resolver.get_contents_debug_adapter_protocol(dct))
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
check_len_entry(len_entry, (GENERATED_LEN_ATTR_NAME, 2))
|
||||
if IS_PY36_OR_GREATER:
|
||||
|
|
@ -25,13 +35,13 @@ def test_dict_resolver():
|
|||
assert contents_debug_adapter_protocol == [("'22'", 22, "['22']"), ("(1, 2)", 2, "[(1, 2)]")]
|
||||
|
||||
|
||||
def test_dict_resolver_hex():
|
||||
from _pydevd_bundle.pydevd_resolver import DictResolver
|
||||
def test_mapping_resolver_hex():
|
||||
from _pydevd_bundle.pydevd_resolver import MappingResolver
|
||||
|
||||
dict_resolver = DictResolver()
|
||||
mapping_resolver = MappingResolver()
|
||||
dct = {(1, 10, 100): (10000, 100000, 100000)}
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(
|
||||
dict_resolver.get_contents_debug_adapter_protocol(dct, fmt={"hex": True})
|
||||
mapping_resolver.get_contents_debug_adapter_protocol(dct, fmt={"hex": True})
|
||||
)
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
check_len_entry(len_entry, (GENERATED_LEN_ATTR_NAME, 1))
|
||||
|
|
@ -164,13 +174,14 @@ def clear_contents_dictionary(dictionary):
|
|||
return dictionary
|
||||
|
||||
|
||||
def test_tuple_resolver():
|
||||
from _pydevd_bundle.pydevd_resolver import TupleResolver
|
||||
@pytest.mark.parametrize("seq_cls", [list, tuple, UserList])
|
||||
def test_sequence_resolver(seq_cls: type[Sequence]):
|
||||
from _pydevd_bundle.pydevd_resolver import SequenceResolver
|
||||
|
||||
tuple_resolver = TupleResolver()
|
||||
seq_resolver = SequenceResolver()
|
||||
fmt = {"hex": True}
|
||||
lst = tuple(range(11))
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(tuple_resolver.get_contents_debug_adapter_protocol(lst))
|
||||
lst = seq_cls(range(11))
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(seq_resolver.get_contents_debug_adapter_protocol(lst))
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
assert contents_debug_adapter_protocol == [
|
||||
("00", 0, "[0]"),
|
||||
|
|
@ -187,7 +198,7 @@ def test_tuple_resolver():
|
|||
]
|
||||
check_len_entry(len_entry, (GENERATED_LEN_ATTR_NAME, 11))
|
||||
|
||||
assert clear_contents_dictionary(tuple_resolver.get_dictionary(lst)) == {
|
||||
assert clear_contents_dictionary(seq_resolver.get_dictionary(lst)) == {
|
||||
"00": 0,
|
||||
"01": 1,
|
||||
"02": 2,
|
||||
|
|
@ -202,9 +213,9 @@ def test_tuple_resolver():
|
|||
GENERATED_LEN_ATTR_NAME: 11,
|
||||
}
|
||||
|
||||
lst = tuple(range(17))
|
||||
lst = seq_cls(range(17))
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(
|
||||
tuple_resolver.get_contents_debug_adapter_protocol(lst, fmt=fmt)
|
||||
seq_resolver.get_contents_debug_adapter_protocol(lst, fmt=fmt)
|
||||
)
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
assert contents_debug_adapter_protocol == [
|
||||
|
|
@ -228,7 +239,7 @@ def test_tuple_resolver():
|
|||
]
|
||||
check_len_entry(len_entry, (GENERATED_LEN_ATTR_NAME, 17))
|
||||
|
||||
assert clear_contents_dictionary(tuple_resolver.get_dictionary(lst, fmt=fmt)) == {
|
||||
assert clear_contents_dictionary(seq_resolver.get_dictionary(lst, fmt=fmt)) == {
|
||||
"0x00": 0,
|
||||
"0x01": 1,
|
||||
"0x02": 2,
|
||||
|
|
@ -249,8 +260,8 @@ def test_tuple_resolver():
|
|||
GENERATED_LEN_ATTR_NAME: 17,
|
||||
}
|
||||
|
||||
lst = tuple(range(10))
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(tuple_resolver.get_contents_debug_adapter_protocol(lst))
|
||||
lst = seq_cls(range(10))
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(seq_resolver.get_contents_debug_adapter_protocol(lst))
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
assert contents_debug_adapter_protocol == [
|
||||
("0", 0, "[0]"),
|
||||
|
|
@ -266,7 +277,7 @@ def test_tuple_resolver():
|
|||
]
|
||||
check_len_entry(len_entry, (GENERATED_LEN_ATTR_NAME, 10))
|
||||
|
||||
assert clear_contents_dictionary(tuple_resolver.get_dictionary(lst)) == {
|
||||
assert clear_contents_dictionary(seq_resolver.get_dictionary(lst)) == {
|
||||
"0": 0,
|
||||
"1": 1,
|
||||
"2": 2,
|
||||
|
|
@ -281,7 +292,7 @@ def test_tuple_resolver():
|
|||
}
|
||||
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(
|
||||
tuple_resolver.get_contents_debug_adapter_protocol(lst, fmt=fmt)
|
||||
seq_resolver.get_contents_debug_adapter_protocol(lst, fmt=fmt)
|
||||
)
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
assert contents_debug_adapter_protocol == [
|
||||
|
|
@ -298,7 +309,7 @@ def test_tuple_resolver():
|
|||
]
|
||||
check_len_entry(len_entry, (GENERATED_LEN_ATTR_NAME, 10))
|
||||
|
||||
assert clear_contents_dictionary(tuple_resolver.get_dictionary(lst, fmt=fmt)) == {
|
||||
assert clear_contents_dictionary(seq_resolver.get_dictionary(lst, fmt=fmt)) == {
|
||||
"0x0": 0,
|
||||
"0x1": 1,
|
||||
"0x2": 2,
|
||||
|
|
@ -313,17 +324,17 @@ def test_tuple_resolver():
|
|||
}
|
||||
|
||||
|
||||
def test_tuple_resolver_mixed():
|
||||
from _pydevd_bundle.pydevd_resolver import TupleResolver
|
||||
def test_sequence_resolver_mixed():
|
||||
from _pydevd_bundle.pydevd_resolver import SequenceResolver
|
||||
|
||||
tuple_resolver = TupleResolver()
|
||||
seq_resolver = SequenceResolver()
|
||||
|
||||
class CustomTuple(tuple):
|
||||
pass
|
||||
|
||||
my_tuple = CustomTuple([1, 2])
|
||||
my_tuple.some_value = 10
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(tuple_resolver.get_contents_debug_adapter_protocol(my_tuple))
|
||||
contents_debug_adapter_protocol = clear_contents_debug_adapter_protocol(seq_resolver.get_contents_debug_adapter_protocol(my_tuple))
|
||||
len_entry = contents_debug_adapter_protocol.pop(-1)
|
||||
check_len_entry(len_entry, (GENERATED_LEN_ATTR_NAME, 2))
|
||||
assert contents_debug_adapter_protocol == [
|
||||
|
|
@ -333,7 +344,7 @@ def test_tuple_resolver_mixed():
|
|||
]
|
||||
|
||||
|
||||
def test_tuple_resolver_ctypes():
|
||||
def test_sequence_resolver_ctypes():
|
||||
import ctypes
|
||||
from _pydevd_bundle.pydevd_xml import get_type
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue