emacs-jupyter / jupyter

An interface to communicate with Jupyter kernels.
GNU General Public License v3.0
944 stars 93 forks source link

IO errors when closing a buffer that had an active async block #531

Open NightMachinery opened 7 months ago

NightMachinery commented 7 months ago

When I close a buffer that has an ongoing async block, I get a lot of these errors:

Jupyter: I/O subscriber error: (wrong-type-argument stringp nil) [82 times]

I need a way to close all the connections of a kernel and "forget" it completely. Here is what I already have, but I don't know how to unsubscribe the active ... "websockets"?

(defun night/jupyter-forget-client (key)
    "Forget a Jupyter client session identified by KEY.
Also kill the associated REPL and object buffers."
    (interactive "sEnter session key to forget: ")

    ;; Remove the client from the `org-babel-jupyter-session-clients' hash table
    (remhash key org-babel-jupyter-session-clients)

    (when-let* ((client (gethash key org-babel-jupyter-session-clients)))
      (let ((repl-buffer (oref client buffer)))
        (message "got repl-buffer")

        ;; Kill the REPL buffer
        (when t ;; (buffer-live-p repl-buffer)
          (message "killing repl-buffer ...")
          ;; (kill-buffer repl-buffer)
          ;; If we use `kill-buffer', the kill hooks will be triggered which will ask us if we want to kill the kernel. We don't, we just want to clear the garbage and leave the kernel alone.
          (night/force-kill-buffer repl-buffer))
        ;; Kill the object buffer
        (jupyter-with-client-buffer client
          (message "got client-buffer")
          (when t ;; (buffer-live-p (current-buffer))
            (message "killing client-buffer ...")
            ;; (kill-buffer (current-buffer))
            (night/force-kill-current-buffer))))

      (message "Forgot session: %s" key)))

(defun night/force-kill-current-buffer ()
  (interactive)
  ;; [[https://emacs.stackexchange.com/questions/59348/force-kill-a-buffer][force kill a buffer? - Emacs Stack Exchange]]
  (let (kill-buffer-hook kill-buffer-query-functions)
    (kill-buffer)))

(defun night/force-kill-buffer (buffer)
  (with-current-buffer buffer
    (night/force-kill-current-buffer)))

Related:

NightMachinery commented 7 months ago

One of the reasons I close buffers is because I have run sth heavy which outputs a lot of text and images. Sometimes I just need this process to finish, and I don't need to see its results in emacs (the results will be logged elsewhere by the code). In these cases, I just want to close the buffer and get rid of the active async blocks, without interrupting or killing the kernel.

nnicandro commented 7 months ago

Although it doesn't work right now, would specifying something like :results none work for you if it did work? Specifying :results none means that the source block is executed, but the results produced by the kernel are not inserted into the buffer and basically don't get processed much.

This worked previously, but doesn't anymore due to c3892b8. I assume now that :results none means a source block is being executed due to resolving some reference to it (e.g. it being used as the value of a :var header argument) which needs to happen synchronously. I have to figure out a more direct way to determine if a source block is being executed due to reference resolution to be able to get :results none :async yes working properly again.

Although specifying :results none would mean messages still get received so there would still be some underlying processing of the messages as they come in.

NightMachinery commented 7 months ago

@nnicandro While :results none can be useful, it's not the ideal solution for my use case. I prefer having the flexibility to dynamically disconnect the output during execution. There are times when I want to see at least the beginning of the output to ensure that the code is on the right track. Checking the logs elsewhere is inconvenient. Moreover, as you mentioned, processing the messages will still be suboptimal, and Emacs will become slow regardless.

Additionally, I need the ability to disconnect the kernel so that I can connect to another kernel running on the same IP and port. (This is possible with SSH port forwarding shenanigans.) It allows me to run the code with different hyperparameters simultaneously.

I understand that I might be stretching the notebook interface beyond its intended use, and it would be better to refactor the code into proper scripts. However, the notebook interface offers more power and ease of use, making it an attractive choice for my current needs.