Add jupyter-server-kernel-names variable and associated functions

Used to associate names to kernel IDs.
This commit is contained in:
Nathaniel Nicandro 2019-07-20 16:56:56 -05:00 committed by Nathaniel Nicandro
parent ea22bd512d
commit 6f97c958da
2 changed files with 200 additions and 13 deletions

View file

@ -71,11 +71,6 @@
;;`jupyter-server' that does not have any websockets open, clients connected,
;; or HTTP connections open, or is not bound to `jupyter-current-server' in any
;; buffer.
;;
;; TODO: Naming kernels in `jupyter-server-list-kernels' instead of using their
;; ID. The kernel ID is not very useful to quickly identify which kernel does
;; what, it would be more useful to be able to associate a name with a kernel
;; ID.
;;; Code:
@ -100,7 +95,7 @@ Used in, e.g. a `jupyter-server-kernel-list-mode' buffer.")
(put 'jupyter-current-server 'permanent-local t)
;;; Plumbing
;;; `jupyter-server'
(defvar jupyter--servers nil)
@ -126,6 +121,8 @@ Access should be done through `jupyter-available-kernelspecs'.")))
(jupyter-comm-stop server))
(delete-instance server))))
;;; `jupyter-server-kernel'
;; TODO: Add the server as a slot
(defclass jupyter-server-kernel (jupyter-meta-kernel)
((server
@ -169,6 +166,88 @@ Access should be done through `jupyter-available-kernelspecs'.")))
(oref id))
9 nil nil "")))
;;; Assigning names to kernel IDs
(defvar jupyter-server-kernel-names nil
"An alist mapping URLs to alists mapping kernel IDs to human friendly names.
For example
\((\"http://localhost:8888\"
(\"72d92ded-1275-440a-852f-90f655197305\" . \"thermo\"))\)
You can persist this alist across Emacs sessions using `desktop',
`savehist', or any other session persistence package. For
example, when using `savehist' you can add the following to your
init file to persist the server names across Emacs sessions.
\(savehist-mode\)
\(add-to-list 'savehist-additional-variables 'jupyter-server-kernel-names\).")
(defun jupyter-server-cull-kernel-names (&optional server)
"Ensure all names in `jupyter-server-kernel-names' map to existing kernels.
If SERVER is non-nil only check the kernels on SERVER, otherwise
check all kernels on all existing servers."
(let ((servers (if server (list server)
(jupyter-gc-servers)
(jupyter-servers))))
(unless server
;; Only remove non-existing servers when culling all kernels on all
;; servers.
(let ((urls (mapcar (lambda (x) (oref x url)) servers)))
(cl-callf2 cl-remove-if-not (lambda (x) (member (car x) urls))
jupyter-server-kernel-names)))
(dolist (server servers)
(when-let* ((names (assoc (oref server url) jupyter-server-kernel-names)))
(setf (alist-get (oref server url)
jupyter-server-kernel-names nil nil #'equal)
(cl-loop
for kernel across (jupyter-api-get-kernel server)
for name = (assoc (plist-get kernel :id) names)
when name collect name))))))
(defun jupyter-server-kernel-name (server id)
"Return the associated name of the kernel with ID on SERVER.
If there is no name associated, return nil. See
`jupyter-server-kernel-names'."
(cl-check-type server jupyter-server)
(let ((kernel-names (assoc (oref server url) jupyter-server-kernel-names)))
(cdr (assoc id kernel-names))))
(defun jupyter-server-kernel-id-from-name (server name)
"Return the ID of the kernel that has NAME on SERVER.
If NAME does not have a kernel associated, return nil. See
`jupyter-server-kernel-names'."
(cl-check-type server jupyter-server)
(jupyter-server-cull-kernel-names server)
(let ((kernel-names (assoc (oref server url) jupyter-server-kernel-names)))
(car (rassoc name kernel-names))))
(defun jupyter-server-name-kernel (server id name)
"NAME the kernel with ID on SERVER.
See `jupyter-server-kernel-names'."
(cl-check-type server jupyter-server)
(setf (alist-get id
(alist-get (oref server url)
jupyter-server-kernel-names
nil nil #'equal)
nil nil #'equal)
name))
(defun jupyter-server-name-client-kernel (client name)
"For the kernel connected to CLIENT associate NAME.
CLIENT must be communicating with a `jupyter-server-kernel', the
ID of the kernel will be associated with NAME, see
`jupyter-server-kernel-names'."
(cl-check-type client jupyter-kernel-client)
(cl-check-type (oref client kcomm) jupyter-server-kernel-comm)
(let* ((kernel (thread-first client
(oref kcomm)
(oref kernel)))
(id (oref kernel id)))
(jupyter-server-name-kernel (oref kernel server) id name)))
;;; Plumbing
;;;; `jupyter-server' events
(cl-defmethod jupyter-event-handler ((comm jupyter-server)
@ -686,6 +765,20 @@ the same meaning as in `jupyter-connect-repl'."
(jupyter-server-launch-kernel jupyter-current-server)
(revert-buffer))
(defun jupyter-server-kernel-list-name-kernel ()
"Name the kernel under `point'."
(interactive)
(when-let* ((id (tabulated-list-get-id))
(name (read-string
(let ((cname (jupyter-server-kernel-name
jupyter-current-server id)))
(if cname (format "Rename %s to: " cname)
(format "Name kernel [%s]: " id))))))
(when (zerop (length name))
(jupyter-server-kernel-list-name-kernel))
(jupyter-server-name-kernel jupyter-current-server id name)
(revert-buffer)))
(defvar jupyter-server-kernel-list-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c C-i") #'jupyter-server-kernel-list-do-interrupt)
@ -696,8 +789,9 @@ the same meaning as in `jupyter-connect-repl'."
(define-key map [mouse-1] #'jupyter-server-kernel-list-new-repl)
(define-key map (kbd "RET") #'jupyter-server-kernel-list-new-repl)
(define-key map (kbd "C-RET") #'jupyter-server-kernel-list-launch-kernel)
(define-key map (kbd "<return>") #'jupyter-server-kernel-list-new-repl)
(define-key map (kbd "C-<return>") #'jupyter-server-kernel-list-launch-kernel)
(define-key map (kbd "<return>") #'jupyter-server-kernel-list-new-repl)
(define-key map "R" #'jupyter-server-kernel-list-name-kernel)
(define-key map "r" #'revert-buffer)
(define-key map "g" #'revert-buffer)
map))
@ -750,12 +844,14 @@ the same meaning as in `jupyter-connect-repl'."
connections &allow-other-keys)
kernel
(let* ((time (jupyter-decode-time last_activity))
(name
(name (propertize
(or (jupyter-server-kernel-name jupyter-current-server id)
(let ((same (cl-remove-if-not
(lambda (x) (string-prefix-p name x)) names)))
(when same (setq name (format "%s<%d>" name (length same))))
(push name names)
(propertize name 'face 'font-lock-constant-face)))
name))
'face 'font-lock-constant-face))
(activity (propertize (jupyter-format-time-low-res time)
'face 'font-lock-doc-face))
(conns (propertize (number-to-string connections)

View file

@ -434,4 +434,95 @@
(should (not (null session)))
(jupyter-test-kill-buffer session))))))))
;;; Naming kernels
(ert-deftest jupyter-server-cull-kernel-names ()
:tags '(server)
(cl-letf* ((jupyter-server-kernel-names
'(("http://localhost:89812"
("id1" . "name1")
("id2" . "name2"))
("http://localhost:89813"
("id3" . "name3"))
("http://localhost:89814"
("id4" . "name4"))))
(servers (list (jupyter-server :url "http://localhost:89812")
(jupyter-server :url "http://localhost:89813")))
((symbol-function #'jupyter-gc-servers) #'ignore)
((symbol-function #'jupyter-servers) (lambda () servers))
((symbol-function #'jupyter-api-get-kernel)
(lambda (server &rest _)
(cond
((equal (oref server url) "http://localhost:89812")
(vector '(:id "id1")))
((equal (oref server url) "http://localhost:89813")
nil)))))
(jupyter-server-cull-kernel-names (car servers))
(should (equal jupyter-server-kernel-names
'(("http://localhost:89812"
("id1" . "name1"))
("http://localhost:89813"
("id3" . "name3"))
("http://localhost:89814"
("id4" . "name4")))))
(jupyter-server-cull-kernel-names)
(should (equal jupyter-server-kernel-names
'(("http://localhost:89812"
("id1" . "name1"))
("http://localhost:89813"))))))
(ert-deftest jupyter-server-kernel-name ()
:tags '(server)
(let ((jupyter-server-kernel-names
'(("http://localhost:8882"
("id1" . "name1"))))
(server (jupyter-server :url "http://localhost:8882")))
(should (equal (jupyter-server-kernel-name server "id1")
"name1"))
(should (null (jupyter-server-kernel-name server "id2")))))
(ert-deftest jupyter-server-kernel-id-from-name ()
:tags '(server)
(cl-letf (((symbol-function #'jupyter-server-cull-kernel-names) #'ignore)
(jupyter-server-kernel-names
'(("http://localhost:8882"
("id1" . "name1"))))
(server (jupyter-server :url "http://localhost:8882")))
(should (equal (jupyter-server-kernel-id-from-name server "name1")
"id1"))
(should (null (jupyter-server-kernel-id-from-name server "name2")))))
(ert-deftest jupyter-server-name-kernel ()
:tags '(server)
(let ((jupyter-server-kernel-names
'(("http://localhost:8882"
("id1" . "name1"))))
(server (jupyter-server :url "http://localhost:8882")))
(jupyter-server-name-kernel server "id2" "name2")
(should (equal jupyter-server-kernel-names
'(("http://localhost:8882"
("id2" . "name2")
("id1" . "name1")))))
(jupyter-server-name-kernel server "id2" "name3")
(should (equal jupyter-server-kernel-names
'(("http://localhost:8882"
("id2" . "name3")
("id1" . "name1")))))))
(ert-deftest jupyter-server-name-client-kernel ()
:tags '(server)
(let* ((jupyter-server-kernel-names
'(("http://localhost:8882"
("id1" . "name1"))))
(server (jupyter-server :url "http://localhost:8882"))
(client (jupyter-kernel-client)))
(oset client kcomm (jupyter-server-kernel-comm
:kernel (jupyter-server-kernel
:id "id1"
:server server)))
(jupyter-server-name-client-kernel client "foo")
(should (equal jupyter-server-kernel-names
'(("http://localhost:8882"
("id1" . "foo")))))))
;;; jupyter-server-test.el ends here