mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Implement #1441: Editing variable values
Implement #1442: Expression variables
This commit is contained in:
parent
63ce4278bc
commit
ae0f8be63a
4 changed files with 99 additions and 63 deletions
|
|
@ -421,10 +421,42 @@ class Adapter:
|
|||
|
||||
def evaluate_request(self, request: Request):
|
||||
expr = request("expression", str)
|
||||
frameId = request("frameId", int)
|
||||
var = eval.evaluate(expr, frameId)
|
||||
return {"result": var.repr, "variablesReference": var.id}
|
||||
|
||||
frame_id = request("frameId", int)
|
||||
frame = StackFrame.get(frame_id)
|
||||
if frame is None:
|
||||
return request.isnt_valid(f'Invalid "frameId": {frame_id}', silent=True)
|
||||
try:
|
||||
result = frame.evaluate(expr)
|
||||
except BaseException as exc:
|
||||
result = exc
|
||||
return eval.Result(frame, result)
|
||||
|
||||
def setVariable_request(self, request: Request):
|
||||
name = request("name", str)
|
||||
value = request("value", str)
|
||||
container_id = request("variablesReference", int)
|
||||
container = eval.VariableContainer.get(container_id)
|
||||
if container is None:
|
||||
raise request.isnt_valid(f'Invalid "variablesReference": {container_id}')
|
||||
try:
|
||||
return container.set_variable(name, value)
|
||||
except BaseException as exc:
|
||||
raise request.cant_handle(str(exc))
|
||||
|
||||
def setExpression_request(self, request: Request):
|
||||
expr = request("expression", str)
|
||||
value = request("value", str)
|
||||
frame_id = request("frameId", int)
|
||||
frame = StackFrame.get(frame_id)
|
||||
if frame is None:
|
||||
return request.isnt_valid(f'Invalid "frameId": {frame_id}', silent=True)
|
||||
try:
|
||||
frame.evaluate(f"{expr} = ({value})", "exec")
|
||||
result = frame.evaluate(expr)
|
||||
except BaseException as exc:
|
||||
raise request.cant_handle(str(exc))
|
||||
return eval.Result(frame, result)
|
||||
|
||||
def disconnect_request(self, request: Request):
|
||||
Breakpoint.clear()
|
||||
self._tracer.abandon_step()
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ import threading
|
|||
from collections.abc import Iterable
|
||||
from debugpy.server.inspect import inspect
|
||||
from types import FrameType
|
||||
from typing import ClassVar, Dict, Literal, Self
|
||||
from typing import ClassVar, Dict, Self
|
||||
|
||||
type ScopeKind = Literal["global", "nonlocal", "local"]
|
||||
type StackFrame = "debugpy.server.tracing.StackFrame"
|
||||
|
||||
|
||||
|
|
@ -30,7 +29,7 @@ class VariableContainer:
|
|||
self.id = VariableContainer._last_id
|
||||
self._all[self.id] = self
|
||||
|
||||
def __getstate__(self):
|
||||
def __getstate__(self) -> dict[str, object]:
|
||||
return {"variablesReference": self.id}
|
||||
|
||||
def __repr__(self):
|
||||
|
|
@ -44,6 +43,9 @@ class VariableContainer:
|
|||
def variables(self) -> Iterable["Variable"]:
|
||||
raise NotImplementedError
|
||||
|
||||
def set_variable(self, name: str, value: str) -> "Value":
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def invalidate(self, *frames: Iterable[StackFrame]) -> None:
|
||||
with _lock:
|
||||
|
|
@ -56,51 +58,18 @@ class VariableContainer:
|
|||
del self._all[id]
|
||||
|
||||
|
||||
class Scope(VariableContainer):
|
||||
frame: FrameType
|
||||
kind: ScopeKind
|
||||
|
||||
def __init__(self, frame: StackFrame, kind: ScopeKind):
|
||||
super().__init__(frame)
|
||||
self.kind = kind
|
||||
|
||||
def __getstate__(self):
|
||||
state = super().__getstate__()
|
||||
state.update(
|
||||
{
|
||||
"name": self.kind,
|
||||
"presentationHint": self.kind,
|
||||
}
|
||||
)
|
||||
return state
|
||||
|
||||
def variables(self) -> Iterable["Variable"]:
|
||||
match self.kind:
|
||||
case "global":
|
||||
d = self.frame.f_globals
|
||||
case "local":
|
||||
d = self.frame.f_locals
|
||||
case _:
|
||||
raise ValueError(f"Unknown scope kind: {self.kind}")
|
||||
for name, value in d.items():
|
||||
yield Variable(self.frame, name, value)
|
||||
|
||||
|
||||
class Variable(VariableContainer):
|
||||
name: str
|
||||
class Value(VariableContainer):
|
||||
value: object
|
||||
# TODO: evaluateName, memoryReference, presentationHint
|
||||
# TODO: memoryReference, presentationHint
|
||||
|
||||
def __init__(self, frame: StackFrame, name: str, value: object):
|
||||
def __init__(self, frame: StackFrame, value: object):
|
||||
super().__init__(frame)
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def __getstate__(self):
|
||||
def __getstate__(self) -> dict[str, object]:
|
||||
state = super().__getstate__()
|
||||
state.update(
|
||||
{
|
||||
"name": self.name,
|
||||
"value": self.repr,
|
||||
"type": self.typename,
|
||||
}
|
||||
|
|
@ -122,17 +91,52 @@ class Variable(VariableContainer):
|
|||
for child in inspect(self.value).children():
|
||||
yield Variable(self.frame, child.name, child.value)
|
||||
|
||||
def set_variable(self, name: str, value_expr: str) -> "Value":
|
||||
value = self.frame.evaluate(value_expr)
|
||||
if name.startswith("[") and name.endswith("]"):
|
||||
key_expr = name[1:-1]
|
||||
key = self.frame.evaluate(key_expr)
|
||||
self.value[key] = value
|
||||
result = self.value[key]
|
||||
else:
|
||||
setattr(self.value, name, value)
|
||||
result = getattr(self.value, name)
|
||||
return Value(self.frame, result)
|
||||
|
||||
|
||||
def evaluate(expr: str, frame_id: int) -> Variable:
|
||||
from debugpy.server.tracing import StackFrame
|
||||
class Result(Value):
|
||||
def __getstate__(self) -> dict[str, object]:
|
||||
state = super().__getstate__()
|
||||
state["result"] = state.pop("value")
|
||||
return state
|
||||
|
||||
frame = StackFrame.get(frame_id)
|
||||
if frame is None:
|
||||
raise ValueError(f"Invalid frame ID: {frame_id}")
|
||||
fobj = frame.frame_object
|
||||
try:
|
||||
code = compile(expr, "<string>", "eval")
|
||||
result = eval(code, fobj.f_globals, fobj.f_locals)
|
||||
except BaseException as exc:
|
||||
result = exc
|
||||
return Variable(frame, expr, result)
|
||||
|
||||
class Variable(Value):
|
||||
name: str
|
||||
# TODO: evaluateName
|
||||
|
||||
def __init__(self, frame: StackFrame, name: str, value: object):
|
||||
super().__init__(frame, value)
|
||||
self.name = name
|
||||
|
||||
def __getstate__(self) -> dict[str, object]:
|
||||
state = super().__getstate__()
|
||||
state["name"] = self.name
|
||||
return state
|
||||
|
||||
|
||||
class Scope(Variable):
|
||||
frame: FrameType
|
||||
|
||||
def __init__(self, frame: StackFrame, name: str, storage: dict[str, object]):
|
||||
class ScopeObject:
|
||||
def __dir__(self):
|
||||
return list(storage.keys())
|
||||
|
||||
def __getattr__(self, name):
|
||||
return storage[name]
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
storage[name] = value
|
||||
|
||||
super().__init__(frame, name, ScopeObject())
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ class ChildObject:
|
|||
def __init__(self, value: object):
|
||||
self.value = value
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def expr(self, obj: object) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
|
|
@ -72,7 +68,7 @@ class ObjectInspector:
|
|||
except BaseException as exc:
|
||||
value = exc
|
||||
try:
|
||||
if hasattr(type(value), "__call__"):
|
||||
if hasattr(value, "__call__"):
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -339,10 +339,14 @@ class StackFrame:
|
|||
def scopes(self) -> List[Scope]:
|
||||
if self._scopes is None:
|
||||
self._scopes = [
|
||||
Scope(self.frame_object, "local"),
|
||||
Scope(self.frame_object, "global"),
|
||||
Scope(self, "local", self.frame_object.f_locals),
|
||||
Scope(self, "global", self.frame_object.f_globals),
|
||||
]
|
||||
return self._scopes
|
||||
|
||||
def evaluate(self, source: str, mode: Literal["eval", "exec", "single"] = "eval") -> object:
|
||||
code = compile(source, "<string>", mode)
|
||||
return eval(code, self.frame_object.f_globals, self.frame_object.f_locals)
|
||||
|
||||
@classmethod
|
||||
def invalidate(self, thread: Thread):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue