mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Pagination of child variables (#1439)
This commit is contained in:
parent
b2c0b3e106
commit
7ecb6c3a4f
5 changed files with 107 additions and 87 deletions
|
|
@ -42,11 +42,11 @@ def main(args):
|
|||
stdio.close()
|
||||
|
||||
if args.log_stderr:
|
||||
log.stderr.levels |= set(log.LEVELS)
|
||||
log.stderr.levels |= {"info", "warning", "error"}
|
||||
if args.log_dir is not None:
|
||||
log.log_dir = args.log_dir
|
||||
|
||||
log.to_file(prefix="debugpy.adapter")
|
||||
log.to_file(prefix="debugpy.adapter", levels=("info", "warning", "error"))
|
||||
log.describe_environment("debugpy.adapter startup environment:")
|
||||
|
||||
servers.access_token = args.server_access_token
|
||||
|
|
|
|||
|
|
@ -370,9 +370,11 @@ class Adapter:
|
|||
|
||||
def variables_request(self, request: Request):
|
||||
start = request("start", 0)
|
||||
|
||||
count = request("count", int, optional=True)
|
||||
if count == ():
|
||||
count = None
|
||||
|
||||
filter = request("filter", str, optional=True)
|
||||
match filter:
|
||||
case ():
|
||||
|
|
@ -381,10 +383,12 @@ class Adapter:
|
|||
filter = {filter}
|
||||
case _:
|
||||
raise request.isnt_valid(f'Invalid "filter": {filter!r}')
|
||||
|
||||
container_id = request("variablesReference", int)
|
||||
container = eval.VariableContainer.get(container_id)
|
||||
if container is None:
|
||||
raise request.isnt_valid(f'Invalid "variablesReference": {container_id}')
|
||||
|
||||
return {"variables": list(container.variables(filter, start, count))}
|
||||
|
||||
def evaluate_request(self, request: Request):
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import ctypes
|
|||
import itertools
|
||||
import debugpy
|
||||
import threading
|
||||
from collections.abc import Iterable, Set
|
||||
from collections.abc import Iterable, Set
|
||||
from debugpy.common import log
|
||||
from debugpy.server.inspect import inspect
|
||||
from debugpy.server.inspect import ObjectInspector, inspect
|
||||
from typing import ClassVar, Literal, Optional, Self
|
||||
|
||||
type StackFrame = "debugpy.server.tracing.StackFrame"
|
||||
|
|
@ -65,18 +65,22 @@ class VariableContainer:
|
|||
|
||||
class Value(VariableContainer):
|
||||
value: object
|
||||
inspector: ObjectInspector
|
||||
# TODO: memoryReference, presentationHint
|
||||
|
||||
def __init__(self, frame: StackFrame, value: object):
|
||||
super().__init__(frame)
|
||||
self.value = value
|
||||
self.inspector = inspect(value)
|
||||
|
||||
def __getstate__(self) -> dict[str, object]:
|
||||
state = super().__getstate__()
|
||||
state.update(
|
||||
{
|
||||
"value": self.repr,
|
||||
"type": self.typename,
|
||||
"value": self.repr(),
|
||||
"namedVariables": self.inspector.named_children_count(),
|
||||
"indexedVariables": self.inspector.indexed_children_count(),
|
||||
}
|
||||
)
|
||||
return state
|
||||
|
|
@ -88,22 +92,28 @@ class Value(VariableContainer):
|
|||
except:
|
||||
return ""
|
||||
|
||||
@property
|
||||
def repr(self) -> str:
|
||||
return "".join(inspect(self.value).repr())
|
||||
return "".join(self.inspector.repr())
|
||||
|
||||
def variables(
|
||||
self, filter: VariableFilter, start: int = 0, count: Optional[int] = None
|
||||
) -> Iterable["Variable"]:
|
||||
children = inspect(self.value).children(
|
||||
include_attrs=("named" in filter),
|
||||
include_items=("indexed" in filter),
|
||||
)
|
||||
stop = None if count is None else start + count
|
||||
log.info("Computing {0} children of {1!r} in range({2}, {3}).", filter, self, start, stop)
|
||||
log.info(
|
||||
"Computing {0} children of {1!r} in range({2}, {3}).",
|
||||
filter,
|
||||
self,
|
||||
start,
|
||||
stop,
|
||||
)
|
||||
|
||||
children = itertools.chain(
|
||||
self.inspector.named_children() if "named" in filter else (),
|
||||
self.inspector.indexed_children() if "indexed" in filter else (),
|
||||
)
|
||||
children = itertools.islice(children, start, stop)
|
||||
for child in children:
|
||||
yield Variable(self.frame, child.name, child.value)
|
||||
yield Variable(self.frame, child.key, child.value)
|
||||
|
||||
def set_variable(self, name: str, value_expr: str) -> "Value":
|
||||
value = self.frame.evaluate(value_expr)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from collections.abc import Iterable
|
|||
|
||||
|
||||
class ChildObject:
|
||||
name: str
|
||||
key: str
|
||||
value: object
|
||||
|
||||
def __init__(self, value: object):
|
||||
|
|
@ -20,17 +20,47 @@ class ChildObject:
|
|||
raise NotImplementedError
|
||||
|
||||
|
||||
class ChildAttribute(ChildObject):
|
||||
name: str
|
||||
|
||||
class NamedChildObject(ChildObject):
|
||||
def __init__(self, name: str, value: object):
|
||||
super().__init__(value)
|
||||
self.name = name
|
||||
self.key = name
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.key
|
||||
|
||||
def expr(self, parent_expr: str) -> str:
|
||||
return f"({parent_expr}).{self.name}"
|
||||
|
||||
|
||||
class LenChildObject(NamedChildObject):
|
||||
def __init__(self, parent: object):
|
||||
super().__init__("len()", len(parent))
|
||||
|
||||
def expr(self, parent_expr: str) -> str:
|
||||
return f"len({parent_expr})"
|
||||
|
||||
|
||||
class IndexedChildObject(ChildObject):
|
||||
key_object: object
|
||||
indexer: str
|
||||
|
||||
def __init__(self, key: object, value: object):
|
||||
super().__init__(value)
|
||||
self.key_object = key
|
||||
self.indexer = None
|
||||
|
||||
@property
|
||||
def key(self) -> str:
|
||||
if self.indexer is None:
|
||||
key_repr = "".join(inspect(self.key_object).repr())
|
||||
self.indexer = f"[{key_repr}]"
|
||||
return self.indexer
|
||||
|
||||
def expr(self, parent_expr: str) -> str:
|
||||
return f"({parent_expr}){self.key}"
|
||||
|
||||
|
||||
class ObjectInspector:
|
||||
"""
|
||||
Inspects a generic object. Uses builtins.repr() to render values and dir() to enumerate children.
|
||||
|
|
@ -51,34 +81,50 @@ class ObjectInspector:
|
|||
result = "<repr() error>"
|
||||
yield result
|
||||
|
||||
def children(
|
||||
self, *, include_attrs: bool = True, include_items: bool = True
|
||||
) -> Iterable[ChildObject]:
|
||||
return (
|
||||
sorted(self._attributes(), key=lambda var: var.name)
|
||||
if include_attrs
|
||||
else ()
|
||||
)
|
||||
|
||||
def _attributes(self) -> Iterable[ChildObject]:
|
||||
# TODO: group class/instance/function/special
|
||||
def children(self) -> Iterable[ChildObject]:
|
||||
yield from self.named_children()
|
||||
yield from self.indexed_children()
|
||||
|
||||
def indexed_children_count(self) -> int:
|
||||
try:
|
||||
names = dir(self.obj)
|
||||
return len(self.obj)
|
||||
except:
|
||||
names = []
|
||||
for name in names:
|
||||
if name.startswith("__"):
|
||||
continue
|
||||
return 0
|
||||
|
||||
def indexed_children(self) -> Iterable[IndexedChildObject]:
|
||||
return ()
|
||||
|
||||
def named_children_count(self) -> int:
|
||||
return len(tuple(self.named_children()))
|
||||
|
||||
def named_children(self) -> Iterable[NamedChildObject]:
|
||||
def attrs():
|
||||
try:
|
||||
value = getattr(self.obj, name)
|
||||
except BaseException as exc:
|
||||
value = exc
|
||||
try:
|
||||
if hasattr(value, "__call__"):
|
||||
names = dir(self.obj)
|
||||
except:
|
||||
names = ()
|
||||
|
||||
# TODO: group class/instance/function/special
|
||||
for name in names:
|
||||
if name.startswith("__"):
|
||||
continue
|
||||
try:
|
||||
value = getattr(self.obj, name)
|
||||
except BaseException as exc:
|
||||
value = exc
|
||||
try:
|
||||
if hasattr(value, "__call__"):
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
yield NamedChildObject(name, value)
|
||||
|
||||
try:
|
||||
yield LenChildObject(self.obj)
|
||||
except:
|
||||
pass
|
||||
yield ChildAttribute(name, value)
|
||||
|
||||
return sorted(attrs(), key=lambda var: var.name)
|
||||
|
||||
|
||||
def inspect(obj: object) -> ObjectInspector:
|
||||
|
|
|
|||
|
|
@ -8,46 +8,13 @@ from collections.abc import Iterable
|
|||
from itertools import count
|
||||
|
||||
from debugpy.common import log
|
||||
from debugpy.server.inspect import ChildObject, ObjectInspector, inspect
|
||||
from debugpy.server.inspect import ObjectInspector, IndexedChildObject, LenChildObject
|
||||
from debugpy.server.safe_repr import SafeRepr
|
||||
|
||||
|
||||
class ChildLen(ChildObject):
|
||||
name = "len()"
|
||||
|
||||
def __init__(self, parent: object):
|
||||
super().__init__(len(parent))
|
||||
|
||||
def expr(self, parent_expr: str) -> str:
|
||||
return f"len({parent_expr})"
|
||||
|
||||
|
||||
class ChildItem(ChildObject):
|
||||
key: object
|
||||
|
||||
def __init__(self, key: object, value: object):
|
||||
super().__init__(value)
|
||||
self.key = key
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
key_repr = "".join(inspect(self.key).repr())
|
||||
return f"[{key_repr}]"
|
||||
|
||||
def expr(self, parent_expr: str) -> str:
|
||||
return f"({parent_expr}){self.name}"
|
||||
|
||||
|
||||
class SequenceInspector(ObjectInspector):
|
||||
def children(
|
||||
self, *, include_attrs: bool = True, include_items: bool = True
|
||||
) -> Iterable[ChildObject]:
|
||||
yield from super().children(
|
||||
include_attrs=include_attrs, include_items=include_items
|
||||
)
|
||||
if not include_items:
|
||||
return
|
||||
yield ChildLen(self.obj)
|
||||
def indexed_children(self) -> Iterable[IndexedChildObject]:
|
||||
yield from super().indexed_children()
|
||||
try:
|
||||
it = iter(self.obj)
|
||||
except:
|
||||
|
|
@ -60,19 +27,12 @@ class SequenceInspector(ObjectInspector):
|
|||
except:
|
||||
log.exception("Error retrieving next item.")
|
||||
break
|
||||
yield ChildItem(i, item)
|
||||
yield IndexedChildObject(i, item)
|
||||
|
||||
|
||||
class MappingInspector(ObjectInspector):
|
||||
def children(
|
||||
self, *, include_attrs: bool = True, include_items: bool = True
|
||||
) -> Iterable[ChildObject]:
|
||||
yield from super().children(
|
||||
include_attrs=include_attrs, include_items=include_items
|
||||
)
|
||||
if not include_items:
|
||||
return
|
||||
yield ChildLen(self.obj)
|
||||
def indexed_children(self) -> Iterable[IndexedChildObject]:
|
||||
yield from super().indexed_children()
|
||||
try:
|
||||
keys = self.obj.keys()
|
||||
except:
|
||||
|
|
@ -89,7 +49,7 @@ class MappingInspector(ObjectInspector):
|
|||
value = self.obj[key]
|
||||
except BaseException as exc:
|
||||
value = exc
|
||||
yield ChildItem(key, value)
|
||||
yield IndexedChildObject(key, value)
|
||||
|
||||
|
||||
class ListInspector(SequenceInspector):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue