Follow-up to cleaning up websockets

Renaming of functions with better understanding of reconnects.  Under
the original logic, reconnecting blithely created a new session if
the original no longer existed.  Now it will alert the user when this
happens.

Also hopefully fixes #381 as there was a bug of missing argument not
being caught by lisp's undisciplined typing.
This commit is contained in:
dickmao 2018-11-03 16:02:04 -04:00 committed by John Miller
parent c92b8bdfc1
commit ee3b0f095c
10 changed files with 217 additions and 148 deletions

View file

@ -16,7 +16,7 @@ Scenario: not running server locally
Then I should not see "ein:completions--prepare-oinfo"
@reconnect
Scenario: kernel restart succeeds
Scenario: kernel reconnect succeeds
Given new default notebook
When I type "import math"
And I wait for cell to execute
@ -25,14 +25,26 @@ Scenario: kernel restart succeeds
Then I should see "WS closed unexpectedly"
And I switch to buffer like "Untitled"
And header says "Kernel requires reconnect C-c C-r"
And I clear log expr "ein:log-all-buffer-name"
And I press "C-c C-r"
And I wait for the smoke to clear
And header does not say "Kernel requires reconnect C-c C-r"
And I clear log expr "ein:log-all-buffer-name"
And I reconnect kernel
And I switch to log expr "ein:log-all-buffer-name"
Then I should not see "[warn]"
And I should not see "[error]"
And I should see "ein:kernel-start--complete"
And I should see "ein:kernel-retrieve-session--complete"
And I switch to buffer like "Untitled"
And I clear log expr "ein:log-all-buffer-name"
And I restart kernel
And I switch to log expr "ein:log-all-buffer-name"
Then I should not see "[warn]"
And I should not see "[error]"
And I should see "ein:kernel-retrieve-session--complete"
And I switch to buffer like "Untitled"
And I kill kernel
And header says "Kernel requires reconnect C-c C-r"
And I clear log expr "ein:log-all-buffer-name"
And my reconnect is questioned

View file

@ -1,16 +1,27 @@
(When "with no opened notebooks call \"\\(.+\\)\"$"
(When "^with no opened notebooks call \"\\(.+\\)\"$"
(lambda (func)
(cl-letf (((symbol-function 'ein:notebook-opened-buffer-names) #'ignore))
(When (format "I call \"%s\"" func)))))
(When "header \\(does not \\)?says? \"\\(.+\\)\"$"
(When "^header \\(does not \\)?says? \"\\(.+\\)\"$"
(lambda (negate says)
(let ((equal-p (string= says (slot-value (slot-value ein:%notification% 'kernel) 'message))))
(cl-assert (if negate (not equal-p) equal-p)))))
(When "I reconnect kernel$"
(When "^I kill kernel$"
(lambda ()
(ein:notebook-reconnect-kernel)
(ein:kernel-delete-session (ein:$notebook-kernel ein:%notebook%))
(And "I wait for the smoke to clear")))
(When "^my reconnect is questioned"
(lambda ()
(ein:notebook-reconnect-session-command (lambda (notebook session-p)
(assert (not session-p))))))
(When "I restart kernel$"
(lambda ()
(cl-letf (((symbol-function 'y-or-n-p) (lambda (&rest ignore) t)))
(ein:notebook-restart-session-command))
(And "I wait for the smoke to clear")))
(When "I call eldoc-documentation-function$"

View file

@ -228,10 +228,8 @@
;; FIXME: Rewrite `ein:$kernel' using `defclass'. It should ease
;; testing since I can mock I/O using method overriding.
(defstruct ein:$kernel
"Hold kernel variables.
"Should perhaps be named ein:$session. We glom session and kernel as defined by the server as just ein:$kernel in the client.
`ein:$kernel-url-or-port'
URL or port of IPython server.
"
url-or-port
events

View file

@ -161,11 +161,11 @@ the source is in git repository."
:timeout ein:content-query-timeout
:parser 'ein:json-read
:sync ein:force-sync
:complete (apply-partially #'ein:query-kernelspecs--complete url-or-port callback)
:success (apply-partially #'ein:query-kernelspecs--success url-or-port)
:complete (apply-partially #'ein:query-kernelspecs--complete url-or-port)
:success (apply-partially #'ein:query-kernelspecs--success url-or-port callback)
:error (apply-partially #'ein:query-kernelspecs--error url-or-port callback iteration)))
(defun* ein:query-kernelspecs--success (url-or-port
(defun* ein:query-kernelspecs--success (url-or-port callback
&key data symbol-status response
&allow-other-keys)
(let ((ks (list :default (plist-get data :default)))
@ -181,7 +181,8 @@ the source is in git repository."
:language (plist-get (plist-get info :spec)
:language)
:spec (plist-get info :spec)))
ks)))))))
ks))))))
(when callback (funcall callback)))
(defun* ein:query-kernelspecs--error (url-or-port callback iteration
&key response error-thrown
@ -191,13 +192,13 @@ the source is in git repository."
(ein:log 'verbose "Retry kernelspecs #%s in response to %s" iteration (request-response-status-code response))
(ein:query-kernelspecs url-or-port callback (1+ iteration)))
(ein:log 'error
"ein:query-kernelspecs--error %s: ERROR %s DATA %s" url-or-port (car error-thrown) (cdr error-thrown))))
"ein:query-kernelspecs--error %s: ERROR %s DATA %s" url-or-port (car error-thrown) (cdr error-thrown))
(when callback (funcall callback))))
(defun* ein:query-kernelspecs--complete (url-or-port callback &key data response
(defun* ein:query-kernelspecs--complete (url-or-port &key data response
&allow-other-keys
&aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data)))
(ein:log 'debug "ein:query-kernelspecs--complete %s" resp-string)
(when callback (funcall callback)))
(ein:log 'debug "ein:query-kernelspecs--complete %s" resp-string))
(defsubst ein:get-ipython-major-version (vstr)
(if vstr

View file

@ -20,8 +20,11 @@
;; along with ein-kernel.el. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; `ein:kernel' is the proxy class of notebook server state.
;; It agglomerates both the "kernel" and "session" objects of server described here
;; https://github.com/jupyter/jupyter/wiki/Jupyter-Notebook-Server-API
;; It may have been better to keep them separate to allow parallel reasoning with
;; the notebook server, but that time is past.
;;; Code:
@ -95,18 +98,80 @@
:content content
:parent_header (make-hash-table)))
(defun ein:kernel-start (kernel notebook &optional iteration callback)
"Start kernel of the notebook whose id is NOTEBOOK-ID.
(defun* ein:kernel-session-p (notebook callback &optional iteration &aux (kernel (ein:$notebook-kernel notebook)))
"Don't make any changes on the server side. CALLBACK with arity 1, a boolean whether session exists on server."
(unless iteration
(setq iteration 0))
(let ((session-id (ein:$kernel-session-id kernel)))
(ein:query-singleton-ajax
(list 'kernel-session-p session-id)
(ein:url (ein:$kernel-url-or-port kernel) "api/sessions" session-id)
:type "GET"
:sync ein:force-sync
:parser #'ein:json-read
:complete (apply-partially #'ein:kernel-session-p--complete session-id)
:success (apply-partially #'ein:kernel-session-p--success session-id callback)
:error (apply-partially #'ein:kernel-session-p--error notebook callback iteration))))
CALLBACK of arity 0 (e.g., print a message kernel started)"
(assert (and (ein:$notebook-p notebook) (ein:$kernel-p kernel)))
(defun* ein:kernel-session-p--complete (session-id &key data response
&allow-other-keys
&aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data)))
(ein:log 'debug "ein:kernel-session-p--complete %s" resp-string))
(defun* ein:kernel-session-p--error (notebook callback iteration &key error-thrown symbol-status data &allow-other-keys)
(if (ein:aand (plist-get data :message) (search "not found" it))
(when callback (funcall callback nil))
(let* ((max-tries 3)
(tries-left (1- (- max-tries iteration))))
(ein:log 'verbose "ein:kernel-session-p--error [%s], %s tries left"
(car error-thrown) tries-left)
(if (> tries-left 0)
(ein:kernel-session-p notebook callback (1+ iteration))))))
(defun* ein:kernel-session-p--success (session-id callback &key data &allow-other-keys)
(let ((session-p (equal (plist-get data :id) session-id)))
(ein:log 'verbose "ein:kernel-session-p--success: session-id=%s session-p=%s"
session-id session-p)
(when callback (funcall callback session-p))))
(defun* ein:kernel-restart-session (notebook
&aux (kernel (ein:$notebook-kernel notebook)))
"Server side delete of NOTEBOOK session and subsequent restart with all new state"
(ein:kernel-delete-session kernel
(apply-partially
(lambda (notebook*)
(ein:events-trigger (ein:$kernel-events kernel)
'status_restarting.Kernel)
(ein:kernel-retrieve-session notebook* 0
(apply-partially
(lambda (nb)
(with-current-buffer (ein:notebook-buffer nb)
(ein:notification-status-set
(slot-value ein:%notification% 'kernel)
'status_restarted.Kernel)))
notebook*)))
notebook)))
(defun* ein:kernel-retrieve-session (notebook &optional iteration callback
&aux (kernel (ein:$notebook-kernel notebook)))
"Formerly ein:kernel-start, but that was misnomer because 1. the server really starts a session (and an accompanying kernel), and 2. it may not even start a session if one exists for the same path.
The server logic is here (could not find other documentation)
https://github.com/jupyter/notebook/blob/04a686dbaf9dfe553324a03cb9e6f778cf1e3da1/notebook/services/sessions/handlers.py#L56-L81
CALLBACK of arity 0 (e.g., print a message kernel started)
This no longer works in iPython-2.0. Protocol is to create a session for a
notebook, which will automatically create and associate a kernel with the notebook.
"
(unless iteration
(setq iteration 0))
(if (<= (ein:$kernel-api-version kernel) 2)
(error "Api version %s unsupported" (ein:$kernel-api-version kernel))
(let ((kernelspec (ein:$notebook-kernelspec notebook)))
(error "Api %s unsupported" (ein:$kernel-api-version kernel))
(let ((kernelspec (ein:$notebook-kernelspec notebook))
(kernel-id (ein:$kernel-kernel-id kernel)))
(ein:query-singleton-ajax
(list 'kernel-start (ein:$kernel-kernel-id kernel))
(list 'kernel-retrieve-session kernel-id)
(ein:url (ein:$kernel-url-or-port kernel) "api/sessions")
:type "POST"
:data (json-encode
@ -118,55 +183,37 @@ CALLBACK of arity 0 (e.g., print a message kernel started)"
(("name" . ,(ein:$kernelspec-name kernelspec))))))))
(t `(("path" . ,(ein:$notebook-notebook-path notebook))
("type" . "notebook")
,@(if kernelspec
`(("kernel" .
(("name" . ,(ein:$kernelspec-name kernelspec))))))))))
("kernel" .
(,@(if kernelspec
`(("name" . ,(ein:$kernelspec-name kernelspec))))
,@(if kernel-id
`(("id" . ,kernel-id)))))
))))
:sync ein:force-sync
:parser #'ein:json-read
:complete (apply-partially #'ein:kernel-start--complete kernel callback)
:success (apply-partially #'ein:kernel-start--success kernel callback)
:error (apply-partially #'ein:kernel-start--error kernel notebook iteration callback)))))
:complete (apply-partially #'ein:kernel-retrieve-session--complete kernel callback)
:success (apply-partially #'ein:kernel-retrieve-session--success kernel callback)
:error (apply-partially #'ein:kernel-retrieve-session--error notebook iteration callback)))))
(defun ein:kernel-restart (kernel)
"Will not restart kernel if kernel doesn't have a session-id.
Kernel can be dead (as in the websocket died unexpectedly) but must be fully-formed for the restart."
(ein:kernel-delete kernel
(apply-partially
(lambda (kernel* notebook)
(if (ein:kernel-live-p kernel*)
(ein:log 'error "Kernel %s still live!" (ein:$kernel-kernel-id kernel*))
(ein:events-trigger (ein:$kernel-events kernel*)
'status_restarting.Kernel)
(ein:kernel-start kernel* notebook 0
(apply-partially
(lambda (nb)
(with-current-buffer (ein:notebook-buffer nb)
(ein:notification-status-set
(slot-value ein:%notification% 'kernel)
'status_restarted.Kernel)))
notebook))))
kernel (ein:get-notebook-or-error))))
(defun* ein:kernel-start--complete (kernel callback &key data response
&allow-other-keys
(defun* ein:kernel-retrieve-session--complete (kernel callback &key data response
&allow-other-keys
&aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data)))
(ein:log 'debug "ein:kernel-start--complete %s" resp-string))
(ein:log 'debug "ein:kernel-retrieve-session--complete %s" resp-string))
(defun* ein:kernel-start--error (kernel notebook iteration callback &key error-thrown sybmol-status &allow-other-keys)
(defun* ein:kernel-retrieve-session--error (notebook iteration callback &key error-thrown symbol-status &allow-other-keys)
(let* ((max-tries 3)
(tries-left (1- (- max-tries iteration))))
(ein:log 'verbose "ein:kernel-start--error [%s], %s tries left"
(ein:log 'verbose "ein:kernel-retrieve-session--error [%s], %s tries left"
(car error-thrown) tries-left)
(if (> tries-left 0)
(ein:kernel-start kernel notebook (1+ iteration) callback))))
(ein:kernel-retrieve-session notebook (1+ iteration) callback))))
(defun* ein:kernel-start--success (kernel callback &key data &allow-other-keys)
(defun* ein:kernel-retrieve-session--success (kernel callback &key data &allow-other-keys)
(let ((session-id (plist-get data :id)))
(if (plist-get data :kernel)
(setq data (plist-get data :kernel)))
(destructuring-bind (&key id &allow-other-keys) data
(ein:log 'verbose "ein:kernel-start--success: kernel-id=%s session-id=%s"
(ein:log 'verbose "ein:kernel-retrieve-session--success: kernel-id=%s session-id=%s"
id session-id)
(setf (ein:$kernel-kernel-id kernel) id)
(setf (ein:$kernel-session-id kernel) session-id)
@ -255,8 +302,8 @@ See: https://github.com/ipython/ipython/pull/3307"
(ein:$kernel-after-start-hook kernel)))
(defun ein:kernel-disconnect (kernel)
"Disconnect websocket connection to running kernel, but do not
kill the kernel."
"Close websocket connection to running kernel, but do not
delete the kernel on the server side"
(ein:events-trigger (ein:$kernel-events kernel) 'status_disconnected.Kernel)
(ein:aif (ein:$kernel-websocket kernel)
(progn (ein:websocket-close it)
@ -558,32 +605,31 @@ Example::
:success (lambda (&rest ignore)
(ein:log 'info "Sent interruption command.")))))
(defun ein:kernel-delete (kernel &optional callback)
(defun ein:kernel-delete-session (kernel &optional callback)
"Regardless of success or error, we clear all state variables of kernel and funcall CALLBACK of arity 0 (e.g., kernel restart)"
(ein:and-let* ((kernel kernel)
(session-id (ein:$kernel-session-id kernel)))
(ein:and-let* ((session-id (ein:$kernel-session-id kernel)))
(ein:query-singleton-ajax
(list 'kernel-delete session-id)
(list 'kernel-delete-session session-id)
(ein:url (ein:$kernel-url-or-port kernel) "api/sessions" session-id)
:type "DELETE"
:complete (apply-partially #'ein:kernel-delete--complete kernel session-id callback)
:error (apply-partially #'ein:kernel-delete--error session-id callback)
:success (apply-partially #'ein:kernel-delete--success session-id callback))))
:complete (apply-partially #'ein:kernel-delete-session--complete kernel session-id callback)
:error (apply-partially #'ein:kernel-delete-session--error session-id callback)
:success (apply-partially #'ein:kernel-delete-session--success session-id callback))))
(defun* ein:kernel-delete--error (session-id callback
(defun* ein:kernel-delete-session--error (session-id callback
&key response error-thrown
&allow-other-keys)
(ein:log 'error "ein:kernel-delete--error %s: ERROR %s DATA %s"
(ein:log 'error "ein:kernel-delete-session--error %s: ERROR %s DATA %s"
session-id (car error-thrown) (cdr error-thrown)))
(defun* ein:kernel-delete--success (session-id callback &key data symbol-status response
(defun* ein:kernel-delete-session--success (session-id callback &key data symbol-status response
&allow-other-keys)
(ein:log 'verbose "ein:kernel-delete--success: %s deleted" session-id))
(ein:log 'verbose "ein:kernel-delete-session--success: %s deleted" session-id))
(defun* ein:kernel-delete--complete (kernel session-id callback &key data response
&allow-other-keys
(defun* ein:kernel-delete-session--complete (kernel session-id callback &key data response
&allow-other-keys
&aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data)))
(ein:log 'debug "ein:kernel-delete--complete %s" resp-string)
(ein:log 'debug "ein:kernel-delete-session--complete %s" resp-string)
(ein:kernel-disconnect kernel)
(when callback (funcall callback)))

View file

@ -253,7 +253,7 @@ jupyter are concerned, 'localhost:8888' and '127.0.0.1:8888' are
(ein:$notebook-notebook-name notebook))
(setf (ein:$notebook-url-or-port notebook) new-url-or-port)
(with-current-buffer (ein:notebook-buffer notebook)
(ein:notebook-start-kernel notebook)
(ein:notebook-retrieve-session notebook)
(rename-buffer (format ein:notebook-buffer-name-template
(ein:$notebook-url-or-port notebook)
(ein:$notebook-notebook-name notebook)))))
@ -369,7 +369,7 @@ notebook buffer. Let's warn for now to see who is doing this.
(ein:$notebook-notebook-name notebook) (ein:$content-name content))
(ein:notebook-bind-events notebook (ein:events-new))
(ein:notebook-maybe-set-kernelspec notebook (plist-get (ein:$content-raw-content content) :metadata))
(ein:notebook-start-kernel notebook)
(ein:notebook-retrieve-session notebook)
(ein:notebook-from-json notebook (ein:$content-raw-content content)) ; notebook buffer is created here
(setf (ein:$notebook-kernelinfo notebook)
(ein:kernelinfo-new (ein:$notebook-kernel notebook)
@ -485,27 +485,14 @@ notebook buffer."
(ein:$notebook-notebook-name notebook))
(ein:$notebook-events notebook))))
(defun ein:notebook-reconnect-kernel ()
"Assuming server will give us back the old kernel id if we send the same noteobook path. TODO - Need to re-verify."
(interactive)
(ein:aif ein:%notebook%
(progn
(ein:kernel-disconnect (ein:$notebook-kernel it))
(ein:events-trigger (ein:$kernel-events (ein:$notebook-kernel it))
'status_reconnecting.Kernel)
(ein:kernel-start (ein:$notebook-kernel it) it
(apply-partially
(lambda (nb)
(with-current-buffer (ein:notebook-buffer nb)
(ein:notification-status-set
(slot-value ein:%notification% 'kernel)
'status_reconnected.Kernel)))
it)))))
(define-obsolete-function-alias
'ein:notebook-show-in-shared-output
'ein:shared-output-show-code-cell-at-point "0.1.2")
(autoload 'org-toggle-latex-fragment "org")
(defalias 'ein:notebook-toggle-latex-fragment 'org-toggle-latex-fragment
"Borrow from org-mode the rendering of latex overlays")
;;; Kernel related things
@ -537,21 +524,22 @@ notebook buffer."
notebook buffer then the user will be prompted to select an opened notebook."
(interactive
(let* ((notebook (or (ein:get-notebook)
(completing-read
"Select notebook [URL-OR-PORT/NAME]: "
(ido-completing-read
"Select notebook: "
(ein:notebook-opened-buffer-names))))
(kernel-name (completing-read
(kernel-name (ido-completing-read
"Select kernel: "
(ein:list-available-kernels (ein:$notebook-url-or-port notebook)))))
(list notebook kernel-name)))
(setf (ein:$notebook-kernelspec notebook) (ein:get-kernelspec (ein:$notebook-url-or-port notebook)
kernel-name))
(ein:log 'info "Restarting notebook %s with new kernel %s." (ein:$notebook-notebook-name notebook) kernel-name)
(ein:notebook-restart-kernel notebook))
(ein:kernel-restart-session notebook))
;;; This no longer works in iPython-2.0. Protocol is to create a session for a
;;; notebook, which will automatically create and associate a kernel with the notebook.
(defun ein:notebook-start-kernel (notebook)
(defun ein:notebook-retrieve-session (notebook)
"Formerly ein:notebook-start-kernel.
If 'picking up from where we last off', that is, we restart emacs and reconnect to same server, jupyter will hand us back the original, still running session."
(let* ((base-url (concat ein:base-kernel-url "kernels"))
(kernelspec (ein:$notebook-kernelspec notebook))
(kernel (ein:kernel-new (ein:$notebook-url-or-port notebook)
@ -561,23 +549,37 @@ notebook buffer then the user will be prompted to select an opened notebook."
(setf (ein:$notebook-kernel notebook) kernel)
(when (eq (ein:get-mode-for-kernel (ein:$notebook-kernelspec notebook)) 'python)
(ein:pytools-setup-hooks kernel notebook))
(ein:kernel-start kernel notebook)))
(ein:kernel-retrieve-session notebook)))
(defun ein:notebook-restart-kernel (notebook)
(ein:kernel-restart (ein:$notebook-kernel notebook)))
(defun ein:notebook-reconnect-session-command (&optional callback)
"It seems convenient but undisciplined to blithely create a new session if the original one no longer exists. CALLBACK takes notebook and session-p."
(interactive)
(unless callback
(setq callback
(lambda (notebook session-p)
(if (or session-p (y-or-n-p "Session not found. Restart?"))
(ein:kernel-retrieve-session notebook 0
(apply-partially (lambda (nb)
(with-current-buffer (ein:notebook-buffer nb)
(ein:notification-status-set
(slot-value ein:%notification% 'kernel)
'status_reconnected.Kernel)))
notebook))))))
(ein:aif ein:%notebook%
(progn
(ein:kernel-disconnect (ein:$notebook-kernel it))
(ein:events-trigger (ein:$kernel-events (ein:$notebook-kernel it))
'status_reconnecting.Kernel)
(ein:kernel-session-p
it (apply-partially callback it)))))
(autoload 'org-toggle-latex-fragment "org")
(defalias 'ein:notebook-toggle-latex-fragment 'org-toggle-latex-fragment
"Borrow from org-mode the rendering of latex overlays")
(defun ein:notebook-restart-kernel-command ()
"Send request to the server to restart kernel."
(defun ein:notebook-restart-session-command ()
"Delete session on server side. Start new session."
(interactive)
(ein:aif ein:%notebook%
(when (or (not (ein:kernel-live-p (ein:$notebook-kernel it)))
(y-or-n-p "Restart kernel? "))
(ein:notebook-restart-kernel it))
(ein:log 'error "Not in notebook buffer!")))
(if (y-or-n-p "Are you sure? ")
(ein:kernel-restart-session it))
(message "Not in notebook buffer!")))
(define-obsolete-function-alias
'ein:notebook-request-tool-tip-or-help-command
@ -598,11 +600,6 @@ This is equivalent to do ``C-c`` in the console program."
(interactive)
(ein:kernel-interrupt (ein:$notebook-kernel ein:%notebook%)))
(defun ein:notebook-kernel-delete-command ()
(interactive)
(when (y-or-n-p "Delete kernel?")
(ein:kernel-delete (ein:$notebook-kernel ein:%notebook%))))
;; autoexec
(defun ein:notebook-execute-autoexec-cells (notebook)
@ -903,7 +900,7 @@ as usual."
(let ((kernel (ein:$notebook-kernel notebook)))
;; If kernel is live, kill it before closing.
(if (ein:kernel-live-p kernel)
(ein:kernel-delete kernel (apply-partially #'ein:notebook-close notebook))
(ein:kernel-delete-session kernel (apply-partially #'ein:notebook-close notebook))
(ein:notebook-close notebook)))))
(defun ein:fast-content-from-notebook (notebook)
@ -1388,8 +1385,8 @@ This hook is run regardless the actual major mode used."
(define-key map (kbd "C-c C-$") 'ein:tb-show)
(define-key map "\C-c\C-x" nil)
(define-key map "\C-c\C-x\C-l" 'ein:notebook-toggle-latex-fragment)
(define-key map "\C-c\C-x\C-r" 'ein:notebook-restart-kernel-command)
(define-key map "\C-c\C-r" 'ein:notebook-reconnect-kernel)
(define-key map "\C-c\C-x\C-r" 'ein:notebook-restart-session-command)
(define-key map "\C-c\C-r" 'ein:notebook-reconnect-session-command)
(define-key map "\C-c\C-z" 'ein:notebook-kernel-interrupt-command)
(define-key map "\C-c\C-q" 'ein:notebook-kill-kernel-then-close-command)
(define-key map (kbd "C-c C-#") 'ein:notebook-close)
@ -1494,8 +1491,8 @@ This hook is run regardless the actual major mode used."
ein:worksheet-next-input-history))))
("Kernel"
,@(ein:generate-menu
'(("Restart kernel" ein:notebook-restart-kernel-command)
("Reconnect kernel" ein:notebook-reconnect-kernel)
'(("Restart session" ein:notebook-restart-session-command)
("Reconnect session" ein:notebook-reconnect-session-command)
("Switch kernel" ein:notebook-switch-kernel)
("Interrupt kernel" ein:notebook-kernel-interrupt-command))))
("Worksheets [Experimental]"

View file

@ -372,9 +372,10 @@ This function is called via `ein:notebook-after-rename-hook'."
TODO - New and open should be separate, and we should flag an exception if we try to new an existing.
"
(interactive (list (ein:notebooklist-ask-url-or-port)
(completing-read
"Select kernel [default]: "
(ido-completing-read
"Select kernel: "
(ein:list-available-kernels (ein:$notebooklist-url-or-port ein:%notebooklist%)) nil t nil nil "default" nil)))
(let ((path (or path (ein:$notebooklist-path (or ein:%notebooklist%
(ein:notebooklist-list-get url-or-port)))))
@ -436,8 +437,8 @@ You may find the new one in the notebook list." error)
"Open new notebook and rename the notebook."
(interactive (let* ((url-or-port (or (ein:get-url-or-port)
(ein:default-url-or-port)))
(kernelspec (completing-read
"Select kernel [default]: "
(kernelspec (ido-completing-read
"Select kernel: "
(ein:list-available-kernels url-or-port) nil t nil nil "default" nil))
(name (read-from-minibuffer
(format "Notebook name (at %s): " url-or-port))))
@ -651,6 +652,7 @@ You may find the new one in the notebook list." error)
(ein:format-time-string ein:notebooklist-date-format dt))))
(defun render-directory (url-or-port sessions)
;; SESSIONS is a hashtable of path to (session-id . kernel-id) pairs
(with-current-buffer (ein:notebooklist-get-buffer url-or-port)
(widget-insert "\n------------------------------------------\n\n")
(ein:make-sorting-widget "Sort by" ein:notebooklist-sort-field)
@ -712,12 +714,14 @@ You may find the new one in the notebook list." error)
(if (gethash path sessions)
(widget-create
'link
:notify (lexical-let ((session (car (gethash path sessions)))
(nblist ein:%notebooklist%))
:notify (lexical-let ((url-or-port url-or-port)
(path path)
(session (car (gethash path sessions))))
(lambda (&rest ignore)
(run-at-time 1 nil #'ein:notebooklist-reload)
(ein:kernel-delete (make-ein:$kernel :url-or-port (ein:$notebooklist-url-or-port nblist)
:session-id session))))
;; can only stop opened notebooks
(ein:and-let* ((nb (ein:notebook-get-opened-notebook url-or-port path)))
(ein:kernel-delete-session (ein:$notebook-kernel nb)))))
"Stop")
(widget-insert "------"))
(widget-insert " ")

View file

@ -41,12 +41,9 @@
(content (make-ein:$content :url-or-port ein:testing-notebook-dummy-url
:notebook-version 3
:path path)))
;; using dynamically scoped flet instead of cl-flet, where
;; "bindings are lexical... all references to the named functions
;; must appear physically within the body of the cl-flet"
(flet ((pop-to-buffer (buf) buf)
(ein:need-notebook-version (url-or-port) 3)
(ein:notebook-start-kernel (notebook))
(ein:notebook-new-session (notebook))
(ein:notebook-enable-autosaves (notebook)))
(let ((notebook (ein:notebook-new ein:testing-notebook-dummy-url path kernelspec)))
(setf (ein:$notebook-kernel notebook)

View file

@ -3,17 +3,20 @@
(require 'ein-kernel)
(require 'ein-testing-kernel)
(require 'ein-testing-notebook)
(defun eintest:kernel-new (port)
(ein:kernel-new port "/api/kernels"
(get-buffer-create "*eintest: dummy for kernel test*")))
(ert-deftest ein:kernel-restart-check-url ()
(lexical-let* ((kernel (eintest:kernel-new 8888))
(lexical-let* ((notebook (ein:notebook-new ein:testing-notebook-dummy-url "" nil))
(kernel (eintest:kernel-new 8888))
(kernel-id "KERNEL-ID")
(desired-url "http://127.0.0.1:8888/api/sessions/KERNEL-ID")
(dummy-response (make-request-response))
got-url)
(setq (ein:$notebook-kernel notebook) kernel)
(cl-letf (((symbol-function 'request)
(lambda (url &rest ignore) (setq got-url url) dummy-response))
((symbol-function 'set-process-query-on-exit-flag) #'ignore)
@ -21,9 +24,9 @@
((symbol-function 'ein:websocket) (lambda (&rest ignore) (make-ein:$websocket :ws nil :kernel kernel :closed-by-client nil)))
((symbol-function 'ein:events-trigger) #'ignore)
((symbol-function 'ein:get-notebook-or-error) (lambda () (ein:get-notebook))))
(ein:kernel-start--success
(ein:kernel-retrieve-session--success
kernel nil :data (list :ws_url "ws://127.0.0.1:8888" :id kernel-id))
(ein:kernel-restart kernel)
(ein:kernel-restart-session notebook)
(should (equal got-url desired-url)))))
(ert-deftest ein:kernel-interrupt-check-url ()
@ -37,7 +40,7 @@
(ein:kernel-stop-channels (&rest ignore))
(ein:websocket (url kernel on-message on-close on-open) (make-ein:$websocket :ws nil :kernel kernel :closed-by-client nil))
(ein:websocket-open-p (websocket) t))
(ein:kernel-start--success
(ein:kernel-retrieve-session--success
kernel nil :data (list :ws_url "ws://127.0.0.1:8888" :id kernel-id))
(ein:kernel-interrupt kernel)
(should (equal got-url desired-url)))))
@ -52,10 +55,10 @@
(set-process-query-on-exit-flag (process flag))
(ein:kernel-stop-channels (&rest ignore))
(ein:websocket (url kernel on-message on-close on-open) (make-ein:$websocket :ws nil :kernel kernel :closed-by-client nil)))
(ein:kernel-start--success
(ein:kernel-retrieve-session--success
kernel nil :data (list :ws_url "ws://127.0.0.1:8888" :id kernel-id))
(ein:kernel-delete kernel)
(should (equal got-url desired-url)))))
(ein:kernel-delete-session kernel))
(should (equal got-url desired-url))))
;;; Test `ein:kernel-construct-help-string'

View file

@ -968,7 +968,7 @@ defined."
((ein:kernel-live-p
(kernel)
((:input (list kernel) :output t)))
(ein:kernel-delete
(ein:kernel-delete-session
(kernel &optional callback)
((:input (list kernel (apply-partially #'ein:notebook-close notebook))))))
(call-interactively #'ein:notebook-kill-kernel-then-close-command))