Skip to content

How interactive debugging works (old way)

Rich Chiodo edited this page Jul 22, 2022 · 15 revisions

The Interactive Window is used for running cells in a python script. One of the capabilities of running these cells is to debug them.

This page will describe how this debugging is implemented under the covers.

What does debug cell do?

image

This sequence diagram is explained below:

Inject debugpy

The first step is to inject debugpy into kernel. Without knowing what the process id of the kernel is, we need a way to have the kernel ready for attach. We do this by running this code in the kernel:

import debugpy
debugpy.listen(('localhost', 0))

That causes debugpy to start a server and returns the port to listen on. We use this port to generate a launch config. Something that looks like so:

        {
            "type": "python",
            "request": "attach",
            "connect": { "host": "localhost", "port": 5678 },
            "justMyCode": true
        },

Attaching

The launch config generated in the previous step is used to attach the debugger to the running debugpy server (much like done here for launching debugging of a python file).

VS code then transitions to debug mode. It just sits there waiting for an event from the debuggee.

Cell File names

The next step is called out as 'Replace kernel's run cell handler'.

What is that code doing? It's replacing the IPython runcell method with a new one so that we can set an environment variable BEFORE we run a cell.

Specifically this code here:

                predicted_name = __VSCODE_compute_hash(args[1], args[0].execution_count)
                os.environ["IPYKERNEL_CELL_NAME"] = predicted_name

Internally IPython uses the environment variable IPYKERNEL_CELL_NAME to set the name of the pseudo file associated with any code that's run.

Why the special run cell hook?

If we need an environment variable to be set when running a cell, why not just execute some code in the kernel? This diagram might explain why:

image

If we're using cells to change the environment variable, those cells themselves end up with the IPYKERNEL_CELL_NAME set for them. In the example above, if CELL_2 calls into code in CELL_1, the debugger won't know that it was the original cell 1, and not the second "cell 1" where the variable was set.

To work around this problem, we instead patch the kernel so it sets the variable itself as it executes code.

Changing cell hashes

Clone this wiki locally