Move server slot of jupyter-server-kernel-manager to the kernel object

This commit is contained in:
Nathaniel Nicandro 2019-07-01 15:52:09 -05:00 committed by Nathaniel Nicandro
parent 9fe99744fd
commit 239715919f
3 changed files with 76 additions and 68 deletions

View file

@ -129,17 +129,27 @@ Access should be done through `jupyter-available-kernelspecs'.")))
;; TODO: Add the server as a slot ;; TODO: Add the server as a slot
(defclass jupyter-server-kernel (jupyter-meta-kernel) (defclass jupyter-server-kernel (jupyter-meta-kernel)
((id ((server
:type jupyter-server
:initarg :server
:documentation "The kernel server.")
(id
:type string :type string
:initarg :id :initarg :id
:documentation "The kernel ID."))) :documentation "The kernel ID.")))
(cl-defmethod jupyter-kernel-alive-p ((kernel jupyter-server-kernel)) (cl-defmethod jupyter-kernel-alive-p ((kernel jupyter-server-kernel))
(slot-boundp kernel 'id)) (and (slot-boundp kernel 'id)
(slot-boundp kernel 'server)
;; TODO: Cache this call
(condition-case err
(jupyter-api-get-kernel (oref kernel server) (oref kernel id))
(jupyter-api-http-error
(unless (= (cadr err) 404) ; Not Found
(signal (car err) (cdr err)))))))
(cl-defmethod jupyter-start-kernel ((kernel jupyter-server-kernel) server &rest _ignore) (cl-defmethod jupyter-start-kernel ((kernel jupyter-server-kernel) &rest _ignore)
(cl-check-type server jupyter-server) (with-slots (server spec) kernel
(with-slots (spec) kernel
(jupyter-server--verify-kernelspec server spec) (jupyter-server--verify-kernelspec server spec)
(cl-destructuring-bind (&key id &allow-other-keys) (cl-destructuring-bind (&key id &allow-other-keys)
(jupyter-api-start-kernel server (car spec)) (jupyter-api-start-kernel server (car spec))
@ -150,8 +160,7 @@ Access should be done through `jupyter-available-kernelspecs'.")))
(ignore)) (ignore))
(defclass jupyter-server-kernel-comm (jupyter-comm-layer) (defclass jupyter-server-kernel-comm (jupyter-comm-layer)
((server :type jupyter-server :initarg :server) ((kernel :type jupyter-server-kernel :initarg :kernel)))
(kernel :type jupyter-server-kernel :initarg :kernel)))
(cl-defmethod jupyter-comm-id ((comm jupyter-server-kernel-comm)) (cl-defmethod jupyter-comm-id ((comm jupyter-server-kernel-comm))
(format "kid=%s" (truncate-string-to-width (format "kid=%s" (truncate-string-to-width
@ -280,30 +289,33 @@ The kernelspecs are returned in the same form as returned by
If SERVER receives events that have the same kernel ID as the If SERVER receives events that have the same kernel ID as the
kernel associated with COMM, then COMM's `jupyter-event-handler' kernel associated with COMM, then COMM's `jupyter-event-handler'
will receive those events." will receive those events."
(with-slots (server) comm (with-slots (server) (oref comm kernel)
(jupyter-comm-start server) (jupyter-comm-start server)
(jupyter-connect-client server comm))) (jupyter-connect-client server comm)))
(cl-defmethod jupyter-comm-stop ((comm jupyter-server-kernel-comm) &rest _ignore) (cl-defmethod jupyter-comm-stop ((comm jupyter-server-kernel-comm) &rest _ignore)
"Disconnect COMM from receiving server events." "Disconnect COMM from receiving server events."
(jupyter-disconnect-client (oref comm server) comm)) (jupyter-disconnect-client (oref (oref comm kernel) server) comm))
(cl-defmethod jupyter-send ((comm jupyter-server-kernel-comm) event-type &rest event) (cl-defmethod jupyter-send ((comm jupyter-server-kernel-comm) event-type &rest event)
"Use COMM to send an EVENT to the server with type, EVENT-TYPE. "Use COMM to send an EVENT to the server with type, EVENT-TYPE.
SERVER will direct EVENT to the right kernel based on the kernel SERVER will direct EVENT to the right kernel based on the kernel
ID of the kernel associated with COMM." ID of the kernel associated with COMM."
(with-slots (server kernel) comm (with-slots (kernel) comm
(apply #'jupyter-send server event-type (oref kernel id) event))) (unless (jupyter-comm-alive-p comm)
(jupyter-comm-start comm))
(apply #'jupyter-send (oref kernel server) event-type (oref kernel id) event)))
(cl-defmethod jupyter-comm-alive-p ((comm jupyter-server-kernel-comm)) (cl-defmethod jupyter-comm-alive-p ((comm jupyter-server-kernel-comm))
"Return non-nil if COMM can receive server events for its associated kernel." "Return non-nil if COMM can receive server events for its associated kernel."
(with-slots (kernel) comm
(and (jupyter-server-kernel-connected-p (and (jupyter-server-kernel-connected-p
(oref comm server) (oref kernel server)
(oref (oref comm kernel) id)) (oref kernel id))
(catch 'member (catch 'member
(jupyter-comm-client-loop (oref comm server) client (jupyter-comm-client-loop (oref kernel server) client
(when (eq client comm) (when (eq client comm)
(throw 'member t)))))) (throw 'member t)))))))
;; TODO: Remove the need for these methods, they are remnants from an older ;; TODO: Remove the need for these methods, they are remnants from an older
;; implementation. They will need to be removed from `jupyter-kernel-client'. ;; implementation. They will need to be removed from `jupyter-kernel-client'.
@ -316,22 +328,18 @@ ID of the kernel associated with COMM."
;;;; `jupyter-server-kernel-manager' ;;;; `jupyter-server-kernel-manager'
(defclass jupyter-server-kernel-manager (jupyter-kernel-manager-base) (defclass jupyter-server-kernel-manager (jupyter-kernel-manager-base)
((server :type jupyter-server :initarg :server) ((kernel :type jupyter-server-kernel :initarg :kernel)
(kernel :type jupyter-server-kernel :initarg :kernel)
(comm :type jupyter-server-kernel-comm))) (comm :type jupyter-server-kernel-comm)))
(cl-defmethod jupyter-comm-start ((manager jupyter-server-kernel-manager)) (cl-defmethod jupyter-comm-start ((manager jupyter-server-kernel-manager))
"Start a websocket connection to MANAGER's kernel. "Start a websocket connection to MANAGER's kernel.
MANAGER's COMM slot will be set to the `jupyter-comm-layer' MANAGER's COMM slot will be set to the `jupyter-comm-layer'
receiving events on the websocket when this method returns." receiving events on the websocket when this method returns."
(with-slots (kernel server) manager (with-slots (kernel comm) manager
(unless (slot-boundp manager 'comm) (unless (slot-boundp manager 'comm)
(oset manager comm (jupyter-server-kernel-comm (oset manager comm (jupyter-server-kernel-comm :kernel kernel)))
:kernel kernel
:server server)))
(with-slots (comm) manager
(unless (jupyter-comm-alive-p comm) (unless (jupyter-comm-alive-p comm)
(jupyter-comm-start comm))))) (jupyter-comm-start comm))))
(cl-defmethod jupyter-comm-stop ((manager jupyter-server-kernel-manager)) (cl-defmethod jupyter-comm-stop ((manager jupyter-server-kernel-manager))
"Stop a websocket connection to MANAGER's kernel." "Stop a websocket connection to MANAGER's kernel."
@ -341,30 +349,29 @@ receiving events on the websocket when this method returns."
(jupyter-comm-stop comm))))) (jupyter-comm-stop comm)))))
(cl-defmethod jupyter-kernel-alive-p ((manager jupyter-server-kernel-manager)) (cl-defmethod jupyter-kernel-alive-p ((manager jupyter-server-kernel-manager))
(with-slots (server kernel) manager (jupyter-kernel-alive-p (oref manager kernel)))
(and (jupyter-kernel-alive-p kernel)
(ignore-errors (jupyter-api-get-kernel server (oref kernel id))))))
(cl-defmethod jupyter-start-kernel ((manager jupyter-server-kernel-manager) &rest _ignore) (cl-defmethod jupyter-start-kernel ((manager jupyter-server-kernel-manager) &rest _ignore)
"Ensure that the gateway can receive events from its kernel." "Ensure that the gateway can receive events from its kernel."
(with-slots (server kernel) manager (with-slots (kernel) manager
(unless (jupyter-kernel-alive-p kernel) (unless (jupyter-kernel-alive-p kernel)
(jupyter-start-kernel kernel server)) (jupyter-start-kernel kernel))
(jupyter-comm-start manager))) (jupyter-comm-start manager)))
(cl-defmethod jupyter-interrupt-kernel ((manager jupyter-server-kernel-manager)) (cl-defmethod jupyter-interrupt-kernel ((manager jupyter-server-kernel-manager))
(with-slots (server kernel) manager (with-slots (kernel) manager
(jupyter-api-interrupt-kernel server (oref kernel id)))) (jupyter-api-interrupt-kernel (oref kernel server) (oref kernel id))))
(cl-defmethod jupyter-kill-kernel ((manager jupyter-server-kernel-manager)) (cl-defmethod jupyter-kill-kernel ((manager jupyter-server-kernel-manager))
(jupyter-shutdown-kernel manager)) (jupyter-shutdown-kernel manager))
(cl-defmethod jupyter-shutdown-kernel ((manager jupyter-server-kernel-manager) &optional restart _timeout) (cl-defmethod jupyter-shutdown-kernel ((manager jupyter-server-kernel-manager) &optional restart _timeout)
(with-slots (server kernel comm) manager (with-slots (kernel) manager
(let ((server (oref kernel server)))
(if restart (jupyter-api-restart-kernel server (oref kernel id)) (if restart (jupyter-api-restart-kernel server (oref kernel id))
(jupyter-comm-stop manager) (jupyter-comm-stop manager)
(when (jupyter-kernel-alive-p manager) (when (jupyter-kernel-alive-p manager)
(jupyter-api-shutdown-kernel server (oref kernel id)))))) (jupyter-api-shutdown-kernel server (oref kernel id)))))))
(cl-defmethod jupyter-make-client ((manager jupyter-server-kernel-manager) _class &rest _slots) (cl-defmethod jupyter-make-client ((manager jupyter-server-kernel-manager) _class &rest _slots)
(let ((client (cl-call-next-method))) (let ((client (cl-call-next-method)))
@ -380,9 +387,10 @@ Return nil if none could be found."
(cl-loop (cl-loop
for manager in (jupyter-kernel-managers) for manager in (jupyter-kernel-managers)
thereis (and (cl-typep manager 'jupyter-server-kernel-manager) thereis (and (cl-typep manager 'jupyter-server-kernel-manager)
(eq (oref manager server) server)
(jupyter-kernel-alive-p manager) (jupyter-kernel-alive-p manager)
(equal (oref (oref manager kernel) id) id) (with-slots (kernel) manager
(and (eq (oref kernel server) server)
(equal (oref kernel id) id)))
manager))) manager)))
(defun jupyter-find-server (url &optional ws-url) (defun jupyter-find-server (url &optional ws-url)
@ -475,6 +483,7 @@ a URL."
'jupyter-server-kernel-comm) 'jupyter-server-kernel-comm)
(thread-first jupyter-current-client (thread-first jupyter-current-client
(oref kcomm) (oref kcomm)
(oref kernel)
(oref server)))) (oref server))))
;; Server of the current TRAMP remote context ;; Server of the current TRAMP remote context
((and (file-remote-p default-directory) ((and (file-remote-p default-directory)
@ -531,10 +540,9 @@ see jupyter-make-client."
(or client-class (setq client-class 'jupyter-kernel-client)) (or client-class (setq client-class 'jupyter-kernel-client))
(let* ((specs (jupyter-server-kernelspecs server)) (let* ((specs (jupyter-server-kernelspecs server))
(kernel (jupyter-server-kernel (kernel (jupyter-server-kernel
:spec (jupyter-guess-kernelspec kernel-name specs)))
(manager (jupyter-server-kernel-manager
:server server :server server
:kernel kernel))) :spec (jupyter-guess-kernelspec kernel-name specs)))
(manager (jupyter-server-kernel-manager :kernel kernel)))
;; Needs to be started before calling `jupyter-make-client' since that ;; Needs to be started before calling `jupyter-make-client' since that
;; method will send a request to start a websocket channel to the kernel. ;; method will send a request to start a websocket channel to the kernel.
;; FIXME: This should be done in a `jupyter-initialize-connection' method, ;; FIXME: This should be done in a `jupyter-initialize-connection' method,
@ -599,13 +607,12 @@ the same meaning as in `jupyter-connect-repl'."
(let* ((specs (jupyter-server-kernelspecs server)) (let* ((specs (jupyter-server-kernelspecs server))
(manager (manager
(or (jupyter-server-find-manager server kernel-id) (or (jupyter-server-find-manager server kernel-id)
(let* ((model (jupyter-api-get-kernel server kernel-id)) (let ((model (jupyter-api-get-kernel server kernel-id)))
(kernel (jupyter-server-kernel
:id kernel-id
:spec (assoc (plist-get model :name) specs))))
(jupyter-server-kernel-manager (jupyter-server-kernel-manager
:kernel (jupyter-server-kernel
:id kernel-id
:server server :server server
:kernel kernel)))) :spec (assoc (plist-get model :name) specs))))))
(client (jupyter-make-client manager client-class))) (client (jupyter-make-client manager client-class)))
(jupyter-start-channels client) (jupyter-start-channels client)
(jupyter-bootstrap-repl client repl-name associate-buffer display))) (jupyter-bootstrap-repl client repl-name associate-buffer display)))

View file

@ -250,7 +250,6 @@
(should (jupyter-api-get-kernel server id)) (should (jupyter-api-get-kernel server id))
(ert-info ("Connecting kernel comm to server") (ert-info ("Connecting kernel comm to server")
(let ((kcomm (jupyter-server-kernel-comm (let ((kcomm (jupyter-server-kernel-comm
:server server
:kernel kernel))) :kernel kernel)))
(should-not (jupyter-server-kernel-connected-p server id)) (should-not (jupyter-server-kernel-connected-p server id))
(jupyter-connect-client server kcomm) (jupyter-connect-client server kcomm)
@ -261,7 +260,6 @@
(should (jupyter-comm-alive-p server)))) (should (jupyter-comm-alive-p server))))
(ert-info ("Connecting kernel comm starts server comm if necessary") (ert-info ("Connecting kernel comm starts server comm if necessary")
(let ((kcomm (jupyter-server-kernel-comm (let ((kcomm (jupyter-server-kernel-comm
:server server
:kernel kernel))) :kernel kernel)))
(jupyter-comm-stop server) (jupyter-comm-stop server)
(should-not (jupyter-comm-alive-p server)) (should-not (jupyter-comm-alive-p server))
@ -274,30 +272,32 @@
(ert-deftest jupyter-server-kernel () (ert-deftest jupyter-server-kernel ()
:tags '(kernel server) :tags '(kernel server)
(let ((kernel (jupyter-server-kernel))) (jupyter-test-with-notebook server
(let ((kernel (jupyter-server-kernel
:server server
:spec (jupyter-guess-kernelspec
"python" (jupyter-server-kernelspecs server)))))
(should (slot-boundp kernel 'server))
(should (eq (oref kernel server) server))
(should-not (slot-boundp kernel 'id)) (should-not (slot-boundp kernel 'id))
(should-not (jupyter-kernel-alive-p kernel)) (should-not (jupyter-kernel-alive-p kernel))
;; TODO: How should this work? Pass the server as an argument? (jupyter-start-kernel kernel)
(should-error (jupyter-start-kernel kernel)) (should (slot-boundp kernel 'id))
(ert-info ("ID slot as a proxy for kernel liveness") (let ((id (oref kernel id)))
(should-not (jupyter-kernel-alive-p kernel)) (unwind-protect
(oset kernel id "foobar") (progn
;; FIXME: There is actually nowhere in the code where the ID slot is made (should (jupyter-api-get-kernel server id))
;; unbound since the event handler of the server relies on the ID slot,
;; but if the kernel is shutdown the necessary communication layer
;; connections are removed.
(should (jupyter-kernel-alive-p kernel))) (should (jupyter-kernel-alive-p kernel)))
(ert-info ("Force killing a server kernel isn't possible") (jupyter-api-shutdown-kernel server id))))))
(should-error (jupyter-kill-kernel kernel)))))
(ert-deftest jupyter-server-kernel-manager () (ert-deftest jupyter-server-kernel-manager ()
:tags '(server) :tags '(server)
(jupyter-test-with-notebook server (jupyter-test-with-notebook server
(let* ((kernel (jupyter-server-kernel (let* ((kernel (jupyter-server-kernel
:server server
:spec (jupyter-guess-kernelspec :spec (jupyter-guess-kernelspec
"python" (jupyter-server-kernelspecs server)))) "python" (jupyter-server-kernelspecs server))))
(manager (jupyter-server-kernel-manager (manager (jupyter-server-kernel-manager
:server server
:kernel kernel))) :kernel kernel)))
(should-not (jupyter-kernel-alive-p manager)) (should-not (jupyter-kernel-alive-p manager))
(jupyter-start-kernel manager) (jupyter-start-kernel manager)

View file

@ -350,9 +350,10 @@ For `url-retrieve', the callback will be called with a nil status."
(declare (indent 3)) (declare (indent 3))
(let ((id (make-symbol "id"))) (let ((id (make-symbol "id")))
`(let ((,kernel (jupyter-server-kernel `(let ((,kernel (jupyter-server-kernel
:server server
:spec (jupyter-guess-kernelspec :spec (jupyter-guess-kernelspec
,name (jupyter-server-kernelspecs ,server))))) ,name (jupyter-server-kernelspecs ,server)))))
(let ((,id (jupyter-start-kernel kernel server))) (let ((,id (jupyter-start-kernel kernel)))
(unwind-protect (unwind-protect
(progn ,@body) (progn ,@body)
(jupyter-api-shutdown-kernel ,server ,id)))))) (jupyter-api-shutdown-kernel ,server ,id))))))