Including kernelspecs into the notebooklist view.

EIN now shows kernels available for the Jupyter server being view in the
notebooklist buffer. We don't do much with the info, yet, though.
This commit is contained in:
John Miller 2016-01-13 07:52:02 -06:00
parent 25d1437de6
commit a89aa6aebd
3 changed files with 159 additions and 119 deletions

View file

@ -47,7 +47,6 @@
url-or-port
events
api-version
kernelspec ; Multiple kernels in Jupyter
session-id
kernel-id
shell-channel
@ -78,10 +77,9 @@
;;; Initialization and connection.
(defun ein:kernel-new (url-or-port base-url events &optional api-version kernelspec)
(defun ein:kernel-new (url-or-port base-url events &optional api-version)
(make-ein:$kernel
:url-or-port url-or-port
:kernelspec (or kernelspec "")
:events events
:api-version (or api-version 2)
:session-id (ein:utils-uuid)
@ -116,26 +114,28 @@
(defun ein:kernel-start (kernel notebook)
"Start kernel of the notebook whose id is NOTEBOOK-ID."
(unless (ein:$kernel-running kernel)
(if (= (ein:$kernel-api-version kernel) 2)
(let ((path (substring (ein:$notebook-notebook-path notebook)
0
(or (position ?/ (ein:$notebook-notebook-path notebook)
:from-end t)
0))))
(ein:kernel-start--legacy kernel
(ein:$notebook-notebook-name notebook)
path))
(ein:query-singleton-ajax
(list 'kernel-start (ein:$kernel-kernel-id kernel))
(ein:url (ein:$kernel-url-or-port kernel)
"api/sessions")
:type "POST"
:data (json-encode `(("notebook" .
(("path" . ,(ein:$notebook-notebook-path notebook))))
(("kernel" .
(("name" . "default"))))))
:parser #'ein:json-read
:success (apply-partially #'ein:kernel--kernel-started kernel)))))
(let ((kernelspec (ein:$notebook-kernelspec notebook)))
(if (= (ein:$kernel-api-version kernel) 2)
(let ((path (substring (ein:$notebook-notebook-path notebook)
0
(or (position ?/ (ein:$notebook-notebook-path notebook)
:from-end t)
0))))
(ein:kernel-start--legacy kernel
(ein:$notebook-notebook-name notebook)
path))
(ein:query-singleton-ajax
(list 'kernel-start (ein:$kernel-kernel-id kernel))
(ein:url (ein:$kernel-url-or-port kernel)
"api/sessions")
:type "POST"
:data (json-encode `(("notebook" .
(("path" . ,(ein:$notebook-notebook-path notebook))))
,@(if kernelspec
`("kernel"
(("name" . ,(ein:$kernelspec-name kernelspec)))))))
:parser #'ein:json-read
:success (apply-partially #'ein:kernel--kernel-started kernel))))))
(defun ein:kernel-start--legacy (kernel notebook-id path)
(unless (ein:$kernel-running kernel)

View file

@ -171,6 +171,9 @@ Current buffer for these functions is set to the notebook buffer.")
`ein:$notebook-kernel' : `ein:$kernel'
`ein:$kernel' instance.
`ein:$notebook-kernelspec' : `ein:$kernelspec'
Jupyter kernel specification for the notebook.
`ein:$notebook-kernelinfo' : `ein:kernelinfo'
`ein:kernelinfo' instance.
@ -209,6 +212,7 @@ Current buffer for these functions is set to the notebook buffer.")
notebook-path
kernel
kernelinfo
kernelspec
pager
dirty
metadata
@ -229,9 +233,12 @@ Current buffer for these functions is set to the notebook buffer.")
;;; Constructor
(defun ein:notebook-new (url-or-port notebook-path &rest args)
(defun ein:notebook-new (url-or-port notebook-path kernelspec &rest args)
(if (or (stringp kernelspec) (symbolp kernelspec))
(setq kernelspec (ein:get-kernelspec url-or-port kernelspec)))
(let ((notebook (apply #'make-ein:$notebook
:url-or-port url-or-port
:kernelspec kernelspec
:notebook-path notebook-path
args)))
notebook))
@ -310,7 +317,7 @@ will be updated with kernel's cwd."
;;; TODO - I think notebook-path is unnecessary (JMM).
(defun ein:notebook-open (url-or-port path &optional callback cbargs)
(defun ein:notebook-open (url-or-port path &optional kernelspec callback cbargs)
"Open notebook at PATH in the server URL-OR-PORT.
Opened notebook instance is returned. Note that notebook might not be
ready at the time when this function is executed.
@ -324,7 +331,6 @@ is newly created or not. When CALLBACK is specified, buffer is
**not** brought up by `pop-to-buffer'. It is caller's
responsibility to do so. The current buffer is set to the
notebook buffer when CALLBACK is called."
;(interactive)
(unless callback (setq callback #'ein:notebook-pop-to-current-buffer))
(let ((buffer (ein:notebook-get-opened-buffer url-or-port path)))
(if (buffer-live-p buffer)
@ -335,9 +341,9 @@ notebook buffer when CALLBACK is called."
(apply callback ein:%notebook% nil cbargs))
ein:%notebook%)
(ein:log 'info "Opening notebook %s..." path)
(ein:notebook-request-open url-or-port path callback cbargs))))
(ein:notebook-request-open url-or-port path kernelspec callback cbargs))))
(defun ein:notebook-request-open (url-or-port path &optional callback cbargs)
(defun ein:notebook-request-open (url-or-port path &optional kernelspec callback cbargs)
"Request notebook at PATH from the server at URL-OR-PORT.
Return an `ein:$notebook' instance. Notebook kernel may not be ready to
receive messages after this call is executed.
@ -345,7 +351,7 @@ receive messages after this call is executed.
CALLBACK is called as \(apply CALLBACK notebook t CBARGS). The second
argument `t' indicates that the notebook is newly opened.
See `ein:notebook-open' for more information."
(let ((notebook (ein:notebook-new url-or-port path)))
(let ((notebook (ein:notebook-new url-or-port path kernelspec)))
(ein:log 'debug "Opening notebook at %s" path)
(ein:content-query-contents path url-or-port nil
(apply-partially #'ein:notebook-request-open-callback-with-callback
@ -435,20 +441,35 @@ of minor mode."
;;; Kernel related things
(defstruct ein:$kernelspec
"Kernel specification as return by the Jupyter notebook server."
"Kernel specification as return by the Jupyter notebook server.
`ein:$kernelspec-name' : string
Name used to identify the kernel (like python2, or python3).
`ein:$kernelspec-resources' : plist
Resources, if any, used by the kernel.
`ein:$kernelspec-spec' : plist
How to start the kernel from the command line. Not used by ein (yet).
"
name
resources
spec)
(defvar ein:available-kernelspecs (make-hash-table))
(defun ein:get-kernelspec (url-or-port name)
(let ((kernelspecs (gethash url-or-port ein:available-kernelspecs))
(name (if (stringp name)
(intern (format ":%s" name))
name)))
(plist-get kernelspecs name)))
(defun ein:list-available-kernels (url-or-port)
(let ((kernelspecs (gethash url-or-port ein:available-kernelspecs)))
(if kernelspecs
(cons "default"
(loop for (key spec) on (ein:plist-exclude kernelspecs '(:default)) by 'cddr
collecting (ein:$kernelspec-name spec)))
"default")))
(loop for (key spec) on (ein:plist-exclude kernelspecs '(:default)) by 'cddr
collecting (ein:$kernelspec-name spec)))))
(defun ein:query-kernelspecs (url-or-port)
"Query jupyter server for the list of available

View file

@ -109,7 +109,7 @@ To suppress popup, you can pass a function `ein:do-nothing' as CALLBACK."
for notebook-path = (plist-get note :path)
when (equal notebook-name name)
return (ein:notebook-open (ein:$notebooklist-url-or-port nblist)
notebook-path callback cbargs)))
notebook-path nil callback cbargs)))
(defun ein:notebooklist-url (url-or-port version &optional path)
(let ((base-path (cond ((= version 2) "api/notebooks")
@ -217,13 +217,17 @@ This function is called via `ein:notebook-after-rename-hook'."
(defun ein:notebooklist-open-notebook (nblist path &optional callback cbargs)
(ein:notebook-open (ein:$notebooklist-url-or-port nblist)
path
nil
callback
cbargs))
;;;###autoload
(defun ein:notebooklist-new-notebook (&optional url-or-port path callback cbargs)
(defun ein:notebooklist-new-notebook (&optional url-or-port kernelspec path callback cbargs)
"Ask server to create a new notebook and open it in a new buffer."
(interactive (list (ein:notebooklist-ask-url-or-port)))
(interactive (list (ein:notebooklist-ask-url-or-port)
(completing-read
"Select kernel [default]: "
(ein:list-available-kernels url-or-port) nil t nil nil "default" nil)))
(let ((path (or path (ein:$notebooklist-path (or ein:%notebooklist%
(ein:notebooklist-list-get url-or-port)))))
(version (ein:$notebooklist-api-version (or ein:%notebooklist%
@ -248,9 +252,10 @@ This function is called via `ein:notebook-after-rename-hook'."
:error (apply-partially #'ein:notebooklist-new-notebook-error
url-or-port path callback cbargs)
:success (apply-partially #'ein:notebooklist-new-notebook-callback
url-or-port path callback cbargs)))))
url-or-port kernelspec path callback cbargs)))))
(defun* ein:notebooklist-new-notebook-callback (url-or-port
kernelspec
path
callback
cbargs
@ -267,7 +272,7 @@ This function is called via `ein:notebook-after-rename-hook'."
(if (string= path "")
(setq path name)
(setq path (format "%s/%s" path name))))
(ein:notebook-open url-or-port path callback cbargs))
(ein:notebook-open url-or-port path kernelspec callback cbargs))
(ein:log 'info (concat "Oops. EIN failed to open new notebook. "
"Please find it in the notebook list."))
(setq no-popup nil))
@ -295,18 +300,19 @@ 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)))
(name (read-from-minibuffer
(format "Notebook name (at %s): " url-or-port)))
(kernelspec (completing-read
"Select kernel [default]: "
(ein:list-available-kernels url-or-port) nil t nil nil "default" nil)))
(list name url-or-port)))
(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))))
(list name kernelspec url-or-port)))
(let ((path (or path (ein:$notebooklist-path
(or ein:%notebooklist%
(ein:get-notebook)
(gethash url-or-port ein:notebooklist-map))))))
(ein:notebooklist-new-notebook
url-or-port
kernelspec
path
(lambda (notebook created name)
(assert created)
@ -375,83 +381,96 @@ Notebook list data is passed via the buffer local variable
path))
name)))
(widget-insert " |\n\n"))
(widget-create
'link
:notify (lambda (&rest ignore) (ein:notebooklist-new-notebook))
"New Notebook")
(widget-insert " ")
(widget-create
'link
:notify (lambda (&rest ignore) (ein:notebooklist-reload))
"Reload List")
(widget-insert " ")
(widget-create
'link
:notify (lambda (&rest ignore)
(browse-url
(ein:url (ein:$notebooklist-url-or-port ein:%notebooklist%))))
"Open In Browser")
(widget-insert "\n\n")
(let* ((kernels (ein:list-available-kernels (ein:$notebooklist-url-or-port ein:%notebooklist%)))
(default-kernel (ein:get-kernelspec (ein:$notebooklist-url-or-port ein:%notebooklist%) (first kernels))))
(widget-create
'link
:notify (lambda (&rest ignore) (call-interactively (ein:notebooklist-new-notebook (ein:$notebooklist-url-or-port ein:%notebooklist%)
default-kernel)))
"New Notebook")
(widget-insert " ")
(widget-create
'link
:notify (lambda (&rest ignore) (ein:notebooklist-reload))
"Reload List")
(widget-insert " ")
(widget-create
'link
:notify (lambda (&rest ignore)
(browse-url
(ein:url (ein:$notebooklist-url-or-port ein:%notebooklist%))))
"Open In Browser")
(widget-insert "\n\nAvailable Kernels:\n")
(let* ((radio-widget (widget-create 'radio-button-choice
:value (first kernels)
:notify (lambda (widget &rest ignore)
(setq default-kernel
(ein:get-kernelspec (ein:$notebooklist-url-or-port ein:%notebooklist%) (widget-value widget)))
(message "New notebooks will be started using the %s kernel."
(widget-value widget))))))
(dolist (k kernels)
(widget-radio-add-item radio-widget (list 'item :value k)))))
(widget-insert "\n")
(let ((api-version (ein:$notebooklist-api-version ein:%notebooklist%))
(sessions (make-hash-table :test 'equal)))
(sessions (make-hash-table :test 'equal)))
(ein:content-query-sessions sessions (ein:$notebooklist-url-or-port ein:%notebooklist%) t)
(loop for note in (ein:$notebooklist-data ein:%notebooklist%)
for urlport = (ein:$notebooklist-url-or-port ein:%notebooklist%)
for name = (plist-get note :name)
for path = (plist-get note :path)
;; (cond ((= 2 api-version)
;; (plist-get note :path))
;; ((= 3 api-version)
;; (ein:get-actual-path (plist-get note :path))))
for type = (plist-get note :type)
for opened-notebook-maybe = (ein:notebook-get-opened-notebook urlport path)
do (widget-insert " ")
if (string= type "directory")
do (progn (widget-create
'link
:notify (lexical-let ((urlport urlport)
(path name))
(lambda (&rest ignore)
(ein:notebooklist-open urlport
(ein:url (ein:$notebooklist-path ein:%notebooklist%)
path))))
"Dir")
(widget-insert " : " name)
(widget-insert "\n"))
if (string= type "notebook")
do (progn (widget-create
'link
:notify (lexical-let ((name name)
(path path))
(lambda (&rest ignore)
(run-at-time 3 nil
#'ein:notebooklist-reload ein:%notebooklist%) ;; TODO using deferred better?
(ein:notebooklist-open-notebook
ein:%notebooklist% path)))
"Open")
(widget-insert " ")
(when (gethash path sessions)
(widget-create
'link
:notify (lexical-let ((session (car (gethash path sessions)))
(nblist ein:%notebooklist%))
(lambda (&rest ignore)
(run-at-time 1 nil
#'ein:notebooklist-reload
ein:%notebooklist%)
(ein:kernel-kill (make-ein:$kernel :url-or-port (ein:$notebooklist-url-or-port nblist)
:session-id session))))
"Stop")
(widget-insert " "))
(widget-create
'link
:notify (lexical-let ((path path))
(lambda (&rest ignore)
(ein:notebooklist-delete-notebook-ask
path)))
"Delete")
(widget-insert " : " name)
(widget-insert "\n"))))
for urlport = (ein:$notebooklist-url-or-port ein:%notebooklist%)
for name = (plist-get note :name)
for path = (plist-get note :path)
;; (cond ((= 2 api-version)
;; (plist-get note :path))
;; ((= 3 api-version)
;; (ein:get-actual-path (plist-get note :path))))
for type = (plist-get note :type)
for opened-notebook-maybe = (ein:notebook-get-opened-notebook urlport path)
do (widget-insert " ")
if (string= type "directory")
do (progn (widget-create
'link
:notify (lexical-let ((urlport urlport)
(path name))
(lambda (&rest ignore)
(ein:notebooklist-open urlport
(ein:url (ein:$notebooklist-path ein:%notebooklist%)
path))))
"Dir")
(widget-insert " : " name)
(widget-insert "\n"))
if (string= type "notebook")
do (progn (widget-create
'link
:notify (lexical-let ((name name)
(path path))
(lambda (&rest ignore)
(run-at-time 3 nil
#'ein:notebooklist-reload ein:%notebooklist%) ;; TODO using deferred better?
(ein:notebooklist-open-notebook
ein:%notebooklist% path)))
"Open")
(widget-insert " ")
(when (gethash path sessions)
(widget-create
'link
:notify (lexical-let ((session (car (gethash path sessions)))
(nblist ein:%notebooklist%))
(lambda (&rest ignore)
(run-at-time 1 nil
#'ein:notebooklist-reload
ein:%notebooklist%)
(ein:kernel-kill (make-ein:$kernel :url-or-port (ein:$notebooklist-url-or-port nblist)
:session-id session))))
"Stop")
(widget-insert " "))
(widget-create
'link
:notify (lexical-let ((path path))
(lambda (&rest ignore)
(ein:notebooklist-delete-notebook-ask
path)))
"Delete")
(widget-insert " : " name)
(widget-insert "\n"))))
(ein:notebooklist-mode)
(widget-setup))
@ -497,7 +516,7 @@ When used in lisp, CALLBACK and CBARGS are passed to `ein:notebook-open'."
(when (and (stringp url-or-port)
(string-match "^[0-9]+$" url-or-port))
(setq url-or-port (string-to-number url-or-port)))
(ein:notebook-open url-or-port path callback cbargs)
(ein:notebook-open url-or-port path nil callback cbargs)
(ein:log 'info "Notebook '%s' not found" nbpath)))
;;;###autoload
@ -565,7 +584,7 @@ upload the current file to the server.
(unless noerror
(assert found nil "No server has notebook named: %s" name))
(destructuring-bind (url-or-port path) found
(ein:notebook-open url-or-port path callback cbargs))))
(ein:notebook-open url-or-port path nil callback cbargs))))
(defvar ein:notebooklist-find-file-buffer-callback #'ignore)