Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add copy_to_globals debug request handling #1055

Merged
merged 3 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion ipykernel/debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,13 @@ class Debugger:
]

# Requests that can be handled even if the debugger is not running
static_debug_msg_types = ["debugInfo", "inspectVariables", "richInspectVariables", "modules"]
static_debug_msg_types = [
"debugInfo",
"inspectVariables",
"richInspectVariables",
"modules",
"copyToGlobals",
]

def __init__(
self, log, debugpy_stream, event_callback, shell_socket, session, just_my_code=True
Expand Down Expand Up @@ -662,6 +668,26 @@ async def richInspectVariables(self, message):
reply["success"] = True
return reply

async def copyToGlobals(self, message):
dst_var_name = message["arguments"]["dstVariableName"]
src_var_name = message["arguments"]["srcVariableName"]
src_frame_id = message["arguments"]["srcFrameId"]

expression = f"globals()['{dst_var_name}']"
seq = message["seq"]
return await self._forward_message(
{
"type": "request",
"command": "setExpression",
"seq": seq + 1,
"arguments": {
"expression": expression,
"value": src_var_name,
"frameId": src_frame_id,
},
}
)

async def modules(self, message):
"""Handle a modules message."""
modules = list(sys.modules.values())
Expand Down
97 changes: 97 additions & 0 deletions ipykernel/tests/test_debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,100 @@ def test_convert_to_long_pathname():
from ipykernel.compiler import _convert_to_long_pathname

_convert_to_long_pathname(__file__)


def test_copy_to_globals(kernel_with_debug):
local_var_name = "var"
global_var_name = "var_copy"
code = f"""from IPython.core.display import HTML
def my_test():
{local_var_name} = HTML('<p>test content</p>')
pass
a = 2
my_test()"""

# Init debugger and set breakpoint
r = wait_for_debug_request(kernel_with_debug, "dumpCell", {"code": code})
source = r["body"]["sourcePath"]

wait_for_debug_request(
kernel_with_debug,
"setBreakpoints",
{
"breakpoints": [{"line": 4}],
"source": {"path": source},
"sourceModified": False,
},
)

wait_for_debug_request(kernel_with_debug, "debugInfo")

wait_for_debug_request(kernel_with_debug, "configurationDone")

# Execute code
kernel_with_debug.execute(code)

# Wait for stop on breakpoint
msg: dict = {"msg_type": "", "content": {}}
while msg.get("msg_type") != "debug_event" or msg["content"].get("event") != "stopped":
msg = kernel_with_debug.get_iopub_msg(timeout=TIMEOUT)

stacks = wait_for_debug_request(kernel_with_debug, "stackTrace", {"threadId": 1})["body"][
"stackFrames"
]

# Get local frame id
frame_id = stacks[0]["id"]

# Copy the variable
wait_for_debug_request(
kernel_with_debug,
"copyToGlobals",
{
"srcVariableName": local_var_name,
"dstVariableName": global_var_name,
"srcFrameId": frame_id,
},
)

# Get the scopes
scopes = wait_for_debug_request(kernel_with_debug, "scopes", {"frameId": frame_id})["body"][
"scopes"
]

# Get the local variable
locals_ = wait_for_debug_request(
kernel_with_debug,
"variables",
{
"variablesReference": next(filter(lambda s: s["name"] == "Locals", scopes))[
"variablesReference"
]
},
)["body"]["variables"]

local_var = None
for variable in locals_:
if local_var_name in variable["evaluateName"]:
local_var = variable
assert local_var is not None

# Get the global variable (copy of the local variable)
globals_ = wait_for_debug_request(
kernel_with_debug,
"variables",
{
"variablesReference": next(filter(lambda s: s["name"] == "Globals", scopes))[
"variablesReference"
]
},
)["body"]["variables"]

global_var = None
for variable in globals_:
if global_var_name in variable["evaluateName"]:
global_var = variable
assert global_var is not None

# Compare local and global variable
assert global_var["value"] == local_var["value"] and global_var["type"] == local_var["type"]
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ classifiers = [
urls = {Homepage = "https://ipython.org"}
requires-python = ">=3.8"
dependencies = [
"debugpy>=1.0",
"debugpy>=1.6.5",
"ipython>=7.23.1",
"comm>=0.1.1",
"traitlets>=5.4.0",
Expand Down