From 8e4a7778ba9deab3755f77d7e8899ee9b78c3fd2 Mon Sep 17 00:00:00 2001 From: Nicolas Brichet Date: Fri, 9 Dec 2022 17:51:18 +0100 Subject: [PATCH 1/3] Add copy_to_globals request --- ipykernel/debugger.py | 28 ++++++++- ipykernel/tests/test_debugger.py | 101 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/ipykernel/debugger.py b/ipykernel/debugger.py index 43ae68300..6d560cde8 100644 --- a/ipykernel/debugger.py +++ b/ipykernel/debugger.py @@ -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 @@ -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()) diff --git a/ipykernel/tests/test_debugger.py b/ipykernel/tests/test_debugger.py index 200154cfc..5260fd399 100644 --- a/ipykernel/tests/test_debugger.py +++ b/ipykernel/tests/test_debugger.py @@ -282,3 +282,104 @@ 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('

test content

') + 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"] From ab4183976e416ec29730cd2189a6a20cf5d49792 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 9 Dec 2022 16:55:33 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ipykernel/debugger.py | 4 ++-- ipykernel/tests/test_debugger.py | 16 ++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ipykernel/debugger.py b/ipykernel/debugger.py index 6d560cde8..b59ad476f 100644 --- a/ipykernel/debugger.py +++ b/ipykernel/debugger.py @@ -321,7 +321,7 @@ class Debugger: "inspectVariables", "richInspectVariables", "modules", - "copyToGlobals" + "copyToGlobals", ] def __init__( @@ -683,7 +683,7 @@ async def copyToGlobals(self, message): "arguments": { "expression": expression, "value": src_var_name, - "frameId": src_frame_id + "frameId": src_frame_id, }, } ) diff --git a/ipykernel/tests/test_debugger.py b/ipykernel/tests/test_debugger.py index 5260fd399..c9071296c 100644 --- a/ipykernel/tests/test_debugger.py +++ b/ipykernel/tests/test_debugger.py @@ -320,11 +320,9 @@ def my_test(): 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"] + stacks = wait_for_debug_request(kernel_with_debug, "stackTrace", {"threadId": 1})["body"][ + "stackFrames" + ] # Get local frame id frame_id = stacks[0]["id"] @@ -341,11 +339,9 @@ def my_test(): ) # Get the scopes - scopes = wait_for_debug_request( - kernel_with_debug, - "scopes", - {"frameId": frame_id} - )["body"]["scopes"] + scopes = wait_for_debug_request(kernel_with_debug, "scopes", {"frameId": frame_id})["body"][ + "scopes" + ] # Get the local variable locals_ = wait_for_debug_request( From 9d83349aee2ee63b59865a0c29c54a890bf75d25 Mon Sep 17 00:00:00 2001 From: Nicolas Brichet Date: Tue, 17 Jan 2023 17:22:54 +0100 Subject: [PATCH 3/3] Update dependency to debugpy to allow the copy-to-globals feature --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1730197a0..ef9587f89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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",