Ensure that f_locals mutation is propagated to actual locals.

This commit is contained in:
Pavel Minaev 2024-03-21 10:17:31 -07:00 committed by Pavel Minaev
parent dfd61c8e72
commit 1d4e23b328
2 changed files with 20 additions and 10 deletions

View file

@ -231,7 +231,7 @@ class Adapter:
raise request.isnt_valid(
f"Unsupported exception breakpoint filter: {filter!r}"
)
break_mode = ExceptionBreakMode.NEVER
if "raised" in filters:
break_mode = ExceptionBreakMode.ALWAYS
@ -391,7 +391,7 @@ class Adapter:
raise request.isnt_valid(f'Unknown thread with "threadId":{thread_id}')
self._tracer.step_over(thread)
return {}
def exceptionInfo_request(self, request: Request):
thread_id = request("threadId", int)
thread = Thread.get(thread_id)
@ -399,7 +399,9 @@ class Adapter:
raise request.isnt_valid(f'Unknown thread with "threadId":{thread_id}')
exc_info = thread.current_exception
if exc_info is None:
raise request.cant_handle(f'No current exception on thread with "threadId":{thread_id}')
raise request.cant_handle(
f'No current exception on thread with "threadId":{thread_id}'
)
return exc_info.__getstate__()
def scopes_request(self, request: Request):
@ -430,7 +432,7 @@ class Adapter:
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)
@ -442,7 +444,7 @@ class Adapter:
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)
@ -456,7 +458,7 @@ class Adapter:
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()

View file

@ -2,11 +2,11 @@
# Licensed under the MIT License. See LICENSE in the project root
# for license information.
import ctypes
import debugpy
import threading
from collections.abc import Iterable
from debugpy.server.inspect import inspect
from types import FrameType
from typing import ClassVar, Dict, Self
type StackFrame = "debugpy.server.tracing.StackFrame"
@ -102,7 +102,7 @@ class Value(VariableContainer):
setattr(self.value, name, value)
result = getattr(self.value, name)
return Value(self.frame, result)
class Result(Value):
def __getstate__(self) -> dict[str, object]:
@ -126,8 +126,6 @@ class Variable(Value):
class Scope(Variable):
frame: FrameType
def __init__(self, frame: StackFrame, name: str, storage: dict[str, object]):
class ScopeObject:
def __dir__(self):
@ -138,5 +136,15 @@ class Scope(Variable):
def __setattr__(self, name, value):
storage[name] = value
# Until PEP 667 is implemented, this is necessary to propagate the changes
# from the dict to actual locals.
try:
PyFrame_LocalsToFast = ctypes.pythonapi.PyFrame_LocalsToFast
except:
pass
else:
PyFrame_LocalsToFast(
ctypes.py_object(frame.frame_object), ctypes.c_int(0)
)
super().__init__(frame, name, ScopeObject())