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

How do I reconnect a kernel whose websocket has been closed without restarting emacs? #526

Open
NightMachinery opened this issue Feb 21, 2024 · 3 comments

Comments

@NightMachinery
Copy link

How do I reconnect a kernel whose websocket has been closed without restarting emacs?

#+begin_src jupyter-python :kernel py_base :session /jpy:127.0.0.1#6035:orgk1/ :async yes :exports both
if: Cannot send message to a closed websocket: #s(websocket-frame text "{\"parent_header\":{},\"header\":{\"msg_id\":\"9f8d9454-ba76-4002-806d-813ee6adddbe\",\"msg_type\":\"execute_request\",\"version\":\"5.3\",\"username\":\"evar\",\"session\":\"609ea724-5330-4426-872a-157b36f19f58\",\"date\":\"2024-02-21T17:37:27.465082+0330\"},\"content\":{\"code\":\"ic(...)\\n\\nNone\",\"silent\":false,\"store_history\":true,\"user_expressions\":{},\"allow_stdin\":true,\"stop_on_error\":false},\"metadata\":{},\"buffers\":[],\"channel\":\"shell\"}" nil t)

I have:

jupyter-server-kernel-names is a variable defined in
jupyter-server.el.

Value
(("http://127.0.0.1:6035"
  ("c3415cb1-c433-4946-8b18-0d61d5355eda" . "orgk1/")))

so when I restart emacs, I can reconnect to this kernel just fine. The kernel is on a remote server, and I am using SSH port forwarding to access it on the localhost. Sometimes the SSH connection gets interrupted, and the websocket is closed. How do I reconnect in such cases without restarting emacs?

@NightMachinery
Copy link
Author

I solved this issue using this function which can "forget" clients:

(defun night/jupyter-forget-client (&optional key)
    "Forget one or multiple Jupyter client sessions.
If KEY is provided, forget that specific session.
Otherwise, present a selection of all session keys to forget."
    (interactive)
    (if key
        ;; If a key is provided, forget that specific key.
        (progn
          (remhash key org-babel-jupyter-session-clients)
          (message "Forgot session: %s" key))

      ;; If no key is provided, let the user select keys to forget.
      (let ((keys (hash-table-keys org-babel-jupyter-session-clients)))
        (ivy-read "Select sessions to forget: " keys
                  :action (lambda (selected)
                            (night/jupyter-forget-client selected))
                  :multi-action (lambda (selected)
                                  (dolist (key selected)
                                    (night/jupyter-forget-client key)))
                  :caller 'night/jupyter-forget-client))))

I can send this as a PR if you're interested.

@nnicandro
Copy link
Collaborator

You can do something like

(jupyter-reauthenticate-websockets (jupyter-current-server))

But this closes and then re-opens every websocket of every kernel for the current server. I'll have to come up with something that is more targeted for a specific client, e.g. re-establish the connection automatically if it's closed unintentionally.

@NightMachinery
Copy link
Author

NightMachinery commented Mar 25, 2024

@nnicandro I actually need a way to "forget" a kernel completely. This is because I use a fixed local port :session /jpy:127.0.0.1#6035:orgk1/, which is port-forwarded to different servers at different times.

I currently use this function, but it seems I am missing some stuff and not properly forgetting/closing everything.

(defun night/jupyter-forget-client (key)
    "Forget a Jupyter client session identified by KEY.
Also kill the associated REPL and object buffers.
@seeAlso `night/counsel-jupyter-forget-client'"
    (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)))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants