jupyter icon indicating copy to clipboard operation
jupyter copied to clipboard

Support for Debugging with Dape

Open Zaijab opened this issue 11 months ago • 0 comments

Aloha,

Thank you for this awesome package. I have a feature request which I think would be nice, but peeking into the current functions I think is really tricky.

I would like to have the jupyter repl support dape breakpoints. I recognize that this is interoperability with two packages so its hard to know who to talk to. However, I see that dape works fine with normal python repls and has a more difficult time with jupyter repls, so perhaps it's more fruitful to ask here first.

I will break down the setup and the expected behavior:

  1. Install dape, and set it up for attaching to a process:
(use-package dape
			:preface
			;; By default dape shares the same keybinding prefix as `gud'
			;; If you do not want to use any prefix, set it to nil.
			;; (setq dape-key-prefix "\C-x\C-a")

			:hook
			;; Save breakpoints on quit
			(kill-emacs . dape-breakpoint-save)
			;; Load breakpoints on startup
			(after-init . dape-breakpoint-load)

			:config
			;; Turn on global bindings for setting breakpoints with mouse
			(dape-breakpoint-global-mode)

			;; Info buffers to the right
			(setq dape-buffer-window-arrangement 'right)

			;; Info buffers like gud (gdb-mi)
			(setq dape-buffer-window-arrangement 'gud)
			(setq dape-info-hide-mode-line nil)

			;; Pulse source line (performance hit)
			(add-hook 'dape-display-source-hook 'pulse-momentary-highlight-one-line)

			;; Showing inlay hints
			(setq dape-inlay-hints t)

			;; Save buffers on startup, useful for interpreted languages
			(add-hook 'dape-start-hook (lambda () (save-some-buffers t t)))
			
			(add-to-list 'dape-configs
				            (list 'debugpy-attach-port-zain
						  'modes '(python-mode python-ts-mode)
						  'port '(lambda () (read-number "Port: " 5678))
						  ':request "attach"
						  ':type "python"
						  ':pathMappings (vector '(:localRoot "/home/zjabbar/code/" :remoteRoot "/home/zjabbar/code/"))
						  ':justMyCode 'nil
						  ':showReturnValue 't))


			;; Kill compile buffer on build success
			(add-hook 'dape-compile-hook 'kill-buffer))
  1. Then open a python file and run-python to get a repl. I use the following:
import time

i = 0


def pr(string):
    print(string, i)


for _ in range(10):
    pr("hello world")
    i += 1
    time.sleep(3)

  1. In the repl type: import debugpy;debugpy.listen(5678).

  2. Now run dape and use my config debugpy-attach-port-zain.

  3. Place a breakpoint (with mouse or M-x dape-breakpoint-toggle) somewhere (I put mine where it says pr("hello world")), and run the code with python-shell-send-buffer (C-c C-c)

You will see that the debugger stops at the breakpoint and continuing works as expected.

Now lets turn to Jupyter, do everything as above but rather than spawning the shell with run-python use jupyter-run-repl. What we see is that upon evaluation of a buffer, the debug process will not stop at a breakpoint. Introducing a breakpoint via inserting debugpy.breakpoint() works and pausing using normal p as well as n for next line works. The only thing that doesn't work is having jupyter know about the breakpoints we insert by clicking on the edge of the window.

As for why I say this is hard, for the internals of jupyter-send-buffer we see that we essentially call eval on the buffer given as a string. We lose some information about the buffer and I'm not sure if that is important for working with dape. The code for python-shell-buffer-substring is much more sophisticated but it works for this purpose.

Zaijab avatar Jan 04 '25 12:01 Zaijab