before embedding notebooklist reloader into ein:notebook-open

This commit is contained in:
dickmao 2020-01-13 11:38:21 -05:00
parent 6ea920dd0f
commit ba530cfa95
7 changed files with 91 additions and 163 deletions

View file

@ -59,7 +59,7 @@ global setting. For global setting and more information, see
:group 'ein)
(defcustom ein:force-sync nil
"If T, force ein to communicate with the IPython/Jupyter contents API synchronously. If not, use asynchronous communication. If you are seeing odd errors while using ein try setting this to T, though note that Emacs will likely be less responsive as it blocks while waiting for the IPython/Jupyter notebook server to respond"
"When non-nil, force synchronous http requests."
:type 'boolean
:group 'ein)
@ -77,7 +77,8 @@ global setting. For global setting and more information, see
params))))
(defun ein:content-query-contents (url-or-port path callback errback &optional iteration)
"Register CALLBACK of arity 1 for the contents at PATH from the URL-OR-PORT. ERRBACK of arity 1 for the contents."
"Register CALLBACK of arity 1 for the contents at PATH from the URL-OR-PORT.
ERRBACK of arity 1 for the contents."
(unless iteration
(setq iteration 0))
(ein:query-singleton-ajax
@ -85,7 +86,6 @@ global setting. For global setting and more information, see
:type "GET"
:timeout ein:content-query-timeout
:parser #'ein:json-read
:sync ein:force-sync
:complete (apply-partially #'ein:content-query-contents--complete url-or-port path)
:success (apply-partially #'ein:content-query-contents--success url-or-port path callback)
:error (apply-partially #'ein:content-query-contents--error url-or-port path callback errback iteration)))
@ -377,7 +377,9 @@ global setting. For global setting and more information, see
;;; Sessions
(defun ein:content-query-sessions (url-or-port callback errback &optional iteration)
"Register CALLBACK of arity 1 to retrieve the sessions. Call ERRBACK of arity 1 (contents) upon failure."
"Register CALLBACK of arity 1 to retrieve the sessions.
Call ERRBACK of arity 1 (contents) upon failure."
(declare (indent defun))
(unless iteration
(setq iteration 0))
(unless callback
@ -390,8 +392,7 @@ global setting. For global setting and more information, see
:parser #'ein:json-read
:complete (apply-partially #'ein:content-query-sessions--complete url-or-port callback)
:success (apply-partially #'ein:content-query-sessions--success url-or-port callback)
:error (apply-partially #'ein:content-query-sessions--error url-or-port callback errback iteration)
:sync ein:force-sync))
:error (apply-partially #'ein:content-query-sessions--error url-or-port callback errback iteration)))
(cl-defun ein:content-query-sessions--success (url-or-port callback &key data &allow-other-keys)
(cl-flet ((read-name (nb-json)

View file

@ -167,7 +167,6 @@ the source is in git repository) or elpa version."
:type "GET"
:timeout ein:content-query-timeout
:parser 'ein:json-read
:sync ein:force-sync
: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)))
@ -228,7 +227,6 @@ the source is in git repository) or elpa version."
(ein:query-singleton-ajax
(ein:url url-or-port "api")
:parser #'ein:json-read
:sync ein:force-sync
:complete (apply-partially #'ein:query-notebook-version--complete url-or-port callback)))
(cl-defun ein:query-notebook-version--complete (url-or-port callback

View file

@ -107,7 +107,6 @@
(ein:query-singleton-ajax
(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 kernel session-id callback)
@ -183,7 +182,6 @@ CALLBACK of arity 1, the kernel.
(("name" . ,(ein:$kernelspec-name kernelspec))
,@(if kernel-id
`(("id" . ,kernel-id)))))))))))
:sync ein:force-sync
:parser #'ein:json-read
:complete (apply-partially #'ein:kernel-retrieve-session--complete kernel callback)
:success (apply-partially #'ein:kernel-retrieve-session--success kernel callback)

View file

@ -233,7 +233,12 @@ combo must match exactly these url/port you used format
(ein:url url-or-port "api/contents" path))))
(defun ein:notebook-open--decorate-callback (notebook existing pending-clear callback no-pop)
"In addition to CALLBACK, also clear the pending semaphore, pop-to-buffer the new notebook, save to disk the kernelspec metadata, and put last warning in minibuffer."
"In addition to CALLBACK,
clear the pending semaphore,
pop-to-buffer the new notebook,
save to disk the kernelspec metadata,
reload the notebooklist so that \"[Stop]\" shows up,
and put last warning in minibuffer."
(apply-partially
(lambda (notebook* created callback* pending-clear* no-pop*)
(funcall pending-clear*)
@ -254,11 +259,12 @@ combo must match exactly these url/port you used format
(ein:notebook-save-notebook notebook*))))
(when callback*
(funcall callback* notebook* created))
(ein:and-let* ((created)
(-when-let* ((created created)
(buffer (get-buffer "*Warnings*"))
(last-warning (with-current-buffer buffer
(thing-at-point 'line t))))
(message "%s" last-warning)))
(message "%s" last-warning))
)
notebook (not existing) callback pending-clear no-pop))
(defun ein:notebook-open-or-create (url-or-port path &optional kernelspec callback no-pop)

View file

@ -47,19 +47,7 @@
:group 'ein
:type 'integer)
(defcustom ein:notebooklist-first-open-hook nil
"Hooks to run when the notebook list is opened at first time.
Example to open a notebook named _scratch_ when the notebook list
is opened at first time.::
(add-hook
'ein:notebooklist-first-open-hook
(lambda () (ein:notebook-open (ein:$notebooklist-url-or-port ein:%notebooklist%) \"main.ipynb\")))
"
:type 'hook
:group 'ein)
(make-obsolete-variable 'ein:notebooklist-first-open-hook nil "0.17.0")
(defstruct ein:$notebooklist
"Hold notebooklist variables.
@ -195,19 +183,19 @@ Return nil if unclear what, if any, authentication applies."
(car-safe url-or-port-list)))))
(ein:url url-or-port)))
(defun ein:notebooklist-open* (url-or-port &optional path resync restore-point-p callback errback)
"The main entry to server at URL-OR-PORT. Users should not directly call this, but instead `ein:notebooklist-login'.
(defun ein:notebooklist-open* (url-or-port &optional path resync callback errback)
"Workhorse of `ein:login'.
A \"notebooklist\" can be opened from any PATH within the server root hierarchy. PATH is empty at the root. RESYNC is requery server attributes such as ipython version and kernelspecs. CALLBACK takes two arguments, the resulting buffer and URL-OR-PORT. ERRBACK takes one argument, the resulting buffer.
A notebooklist can be opened from any PATH within the server root hierarchy.
PATH is empty at the root. RESYNC, when non-nil, requeries the contents-api
version and kernelspecs.
TODO: going to maintain jupyterhub hooks here
"
(unless path (setq path ""))
(setq url-or-port (ein:url url-or-port)) ;; should work towards not needing this
(lexical-let* ((url-or-port url-or-port)
(path path)
CALLBACK takes two arguments, the resulting buffer and URL-OR-PORT.
ERRBACK takes one argument, the resulting buffer."
(lexical-let* ((url-or-port (ein:url url-or-port))
(path (or path ""))
(success (apply-partially #'ein:notebooklist-open--finish
url-or-port restore-point-p callback))
url-or-port callback))
(failure errback))
(if (and (not resync) (ein:notebooklist-list-get url-or-port))
(ein:content-query-contents url-or-port path success failure)
@ -223,19 +211,8 @@ TODO: going to maintain jupyterhub hooks here
(ein:content-query-hierarchy url-or-port #'ignore))))
(ein:content-query-contents url-or-port path success failure))))))))
(defcustom ein:notebooklist-keepalive-refresh-time 1
"When the notebook keepalive is enabled, the frequency, IN
HOURS, with which to make calls to the jupyter content API to
refresh the notebook connection."
:type 'float
:group 'ein)
(defcustom ein:enable-keepalive nil
"When non-nil, will cause EIN to automatically call
`ein:notebooklist-enable-keepalive' after any call to
`ein:notebooklist-open'."
:type 'boolean
:group 'ein)
(make-obsolete-variable 'ein:notebooklist-keepalive-refresh-time nil "0.17.0")
(make-obsolete-variable 'ein:enable-keepalive nil "0.17.0")
(defcustom ein:notebooklist-date-format "%F"
"The format spec for date in notebooklist mode.
@ -243,60 +220,31 @@ See `ein:format-time-string'."
:type '(or string function)
:group 'ein)
(defvar ein:notebooklist--keepalive-timer nil)
;;;###autoload
(defun ein:notebooklist-enable-keepalive (&optional url-or-port)
"Enable periodic calls to the notebook server to keep long running sessions from expiring.
By long running we mean sessions to last days, or weeks. The
frequency of the refresh (which is very similar to a call to
`ein:notebooklist-open`) is controlled by
`ein:notebooklist-keepalive-refresh-time`, and is measured in
terms of hours. If `ein:enable-keepalive' is non-nil this will
automatically be called during calls to `ein:notebooklist-open`."
(interactive (list (ein:notebooklist-ask-url-or-port)))
(unless ein:notebooklist--keepalive-timer
(message "Enabling notebooklist keepalive...")
(let ((success
(lambda (content)
(ein:log 'info "Refreshing notebooklist connection.")))
(refresh-time (* ein:notebooklist-keepalive-refresh-time 60 60)))
(setq ein:notebooklist--keepalive-timer
(run-at-time 0.1 refresh-time #'ein:content-query-contents url-or-port "" success nil)))))
;;;###autoload
(defun ein:notebooklist-disable-keepalive ()
"Disable the notebooklist keepalive calls to the jupyter notebook server."
(interactive)
(message "Disabling notebooklist keepalive...")
(cancel-timer ein:notebooklist--keepalive-timer)
(setq ein:notebooklist--keepalive-timer nil))
(defun ein:notebooklist-open--finish (url-or-port restore-point-p callback content)
(defun ein:notebooklist-open--finish (url-or-port callback content)
"Called via `ein:notebooklist-open*'."
(let ((path (ein:$content-path content))
(nb-version (ein:$content-notebook-version content))
(data (ein:$content-raw-content content)))
(declare (indent defun))
(ein:log 'verbose "Opening notebooklist at %s"
(ein:url url-or-port (ein:$content-path content)))
(with-current-buffer (ein:notebooklist-get-buffer url-or-port)
(let ((already-opened-p (ein:notebooklist-list-get url-or-port))
(orig-point (if restore-point-p
(point)
(point-min))))
(let ((restore-point (ein:aand (widget-at)
(widget-value it)
(string-match-p "Open\\|Stop\\|Delete" it)
(point))))
(aif ein:%notebooklist%
(ein:notebooklist-list-remove (ein:$notebooklist-url-or-port it)))
(setq ein:%notebooklist%
(make-ein:$notebooklist :url-or-port url-or-port
:path path
:data data
:api-version nb-version))
:path (ein:$content-path content)
:data (ein:$content-raw-content content)
:api-version (ein:$content-notebook-version content)))
(ein:notebooklist-list-add ein:%notebooklist%)
(ein:notebooklist-render nb-version orig-point)
(ein:log 'verbose "Opened notebooklist at %s" (ein:url url-or-port path))
(unless already-opened-p
(run-hooks 'ein:notebooklist-first-open-hook))
(when ein:enable-keepalive
(ein:notebooklist-enable-keepalive url-or-port))
(let ((inhibit-read-only t))
(erase-buffer))
(when callback
(funcall callback (current-buffer) url-or-port)))
(current-buffer))))
(funcall callback (current-buffer) url-or-port))
(ein:content-query-sessions url-or-port
(apply-partially #'ein:notebooklist-render url-or-port restore-point)
#'ignore))))
(cl-defun ein:notebooklist-open-error (url-or-port path
&key error-thrown &allow-other-keys)
@ -311,8 +259,7 @@ automatically be called during calls to `ein:notebooklist-open`."
(setq nblist ein:%notebooklist%))
(when nblist
(ein:notebooklist-open* (ein:$notebooklist-url-or-port nblist)
(ein:$notebooklist-path nblist) resync t
callback)))
(ein:$notebooklist-path nblist) resync callback)))
(defun ein:notebooklist-refresh-related ()
"Reload notebook list in which current notebook locates.
@ -367,7 +314,7 @@ This function is called via `ein:notebook-after-rename-hook'."
(setq nbpath nbname)
(setq nbpath (format "%s/%s" nbpath nbname))))
(ein:notebook-open url-or-port nbpath kernelspec callback nil no-pop)
(ein:notebooklist-open* url-or-port path nil t)))
(ein:notebooklist-open* url-or-port path)))
(cl-defun ein:notebooklist-new-notebook-error
(url-or-port kernelspec path callback no-pop retry
@ -496,8 +443,8 @@ This function is called via `ein:notebook-after-rename-hook'."
(widget-create
'link
:notify (lambda (&rest _ignore)
(ein:notebooklist-open* url-or-port path nil nil
(lambda (buffer url-or-port)
(ein:notebooklist-open* url-or-port path nil
(lambda (buffer _url-or-port)
(pop-to-buffer buffer))))
name)))
(widget-insert " |\n\n"))
@ -554,10 +501,6 @@ This function is called via `ein:notebook-after-rename-hook'."
(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)
(ein:make-sorting-widget "In Order" ein:notebooklist-sort-order)
(widget-insert "\n")
(cl-loop with reloader = (apply-partially (lambda (nblist _kernel)
(ein:notebooklist-reload nblist))
ein:%notebooklist%)
@ -571,7 +514,6 @@ This function is called via `ein:notebook-after-rename-hook'."
for type = (plist-get note :type)
for opened-notebook-maybe = (ein:notebook-get-opened-notebook
url-or-port path)
do (widget-insert " ")
if (string= type "directory")
do (progn (widget-create
'link
@ -583,8 +525,8 @@ This function is called via `ein:notebook-after-rename-hook'."
(concat (file-name-as-directory
(ein:$notebooklist-path ein:%notebooklist%))
name)
nil nil
(lambda (buffer url-or-port) (pop-to-buffer buffer)))))
nil
(lambda (buffer _url-or-port) (pop-to-buffer buffer)))))
"Dir")
(widget-insert " : " name)
(widget-insert "\n"))
@ -594,13 +536,7 @@ This function is called via `ein:notebook-after-rename-hook'."
'link
:notify (apply-partially #'ein:file-open url-or-port path)
"Open")
(widget-insert " ------ ")
(widget-create
'link
:notify (lexical-let ((path path))
(lambda (&rest _ignore)
(message "[EIN]: NBlist delete file command. Implement me!")))
"Delete")
(widget-insert " ")
(widget-insert " : " (ein:format-nbitem-data name last-modified))
(widget-insert "\n"))
end
@ -629,7 +565,7 @@ This function is called via `ein:notebook-after-rename-hook'."
:path path*)))
reloader url-or-port path)
"Stop")
(widget-insert "------"))
(widget-insert "[----]"))
(widget-insert " ")
(widget-create
'link
@ -647,30 +583,16 @@ This function is called via `ein:notebook-after-rename-hook'."
(widget-insert "\n"))
end)))
(defun ein:notebooklist-render (nb-version &optional restore-point)
"Render notebook list widget.
Notebook list data is passed via the buffer local variable
`ein:notebooklist-data'."
(kill-all-local-variables)
(let ((inhibit-read-only t))
(erase-buffer))
(remove-overlays)
(let ((url-or-port (ein:$notebooklist-url-or-port ein:%notebooklist%)))
(ein:content-query-sessions url-or-port
(apply-partially #'ein:notebooklist-render--finish nb-version url-or-port restore-point)
nil)))
(defun ein:notebooklist-render--finish (nb-version url-or-port restore-point sessions)
(defun ein:notebooklist-render (url-or-port restore-point sessions)
(with-current-buffer (ein:notebooklist-get-buffer url-or-port)
(render-header url-or-port sessions)
(render-directory url-or-port sessions)
(with-current-buffer (ein:notebooklist-get-buffer url-or-port)
(ein:notebooklist-mode)
(widget-setup)
(goto-char (or restore-point (point-min)))))
(aif (get-buffer-window (current-buffer))
(set-window-point it (or restore-point (point-min))))))
;;;###autoload
(defun ein:notebooklist-list-paths (&optional content-type)
"Return all files of CONTENT-TYPE for all sessions"
(apply #'append
@ -742,7 +664,7 @@ You should setup `ein:url-or-port' in order to make this code work."
(defun ein:notebooklist-open (url-or-port callback)
"This is now an alias for ein:notebooklist-login"
(interactive `(,(ein:notebooklist-ask-url-or-port)
,(lambda (buffer url-or-port) (pop-to-buffer buffer))))
,(lambda (buffer _url-or-port) (pop-to-buffer buffer))))
(ein:notebooklist-login url-or-port callback))
(make-obsolete 'ein:notebooklist-open 'ein:notebooklist-login "0.14.2")
@ -762,7 +684,7 @@ You should setup `ein:url-or-port' in order to make this code work."
CALLBACK takes two arguments, the buffer created by ein:notebooklist-open--success
and the url-or-port argument of ein:notebooklist-open*."
(interactive `(,(ein:notebooklist-ask-url-or-port)
,(lambda (buffer url-or-port) (pop-to-buffer buffer))
,(lambda (buffer _url-or-port) (pop-to-buffer buffer))
,(if current-prefix-arg (ein:notebooklist-ask-user-pw-pair "Cookie name" "Cookie content"))))
(unless callback (setq callback (lambda (buffer url-or-port))))
(when cookie-plist
@ -777,16 +699,17 @@ and the url-or-port argument of ein:notebooklist-open*."
(ein:notebooklist-login--iteration url-or-port callback nil nil -1 nil))
((string= token "") ;; all authentication disabled
(ein:log 'verbose "Skipping login %s" url-or-port)
(ein:notebooklist-open* url-or-port nil nil nil callback nil))
(ein:notebooklist-open* url-or-port nil nil callback nil))
(t (ein:notebooklist-login--iteration url-or-port callback nil token 0 nil)))))
(defun ein:notebooklist-login--parser ()
(save-excursion
(goto-char (point-min))
(list :bad-page (re-search-forward "<input type=.?password" nil t)))
(list :bad-page (re-search-forward "<input type=.?password" nil t))))
(defun ein:notebooklist-login--success-1 (url-or-port callback errback)
(ein:log 'info "Login to %s complete." url-or-port)
(ein:notebooklist-open* url-or-port nil nil nil callback errback))
(ein:notebooklist-open* url-or-port nil nil callback errback))
(defun ein:notebooklist-login--error-1 (url-or-port error-thrown response errback)
(ein:log 'error "Login to %s failed, error-thrown %s, raw-header %s"

View file

@ -119,6 +119,7 @@ aborts). Instead you will see Race! in debug messages.
(ein:query-enforce-curl)
(when timeout
(setq settings (plist-put settings :timeout (/ timeout 1000.0))))
(setq settings (plist-put settings :sync ein:force-sync))
(apply #'request (url-encode-url url) (ein:query-prepare-header url settings)))
(defun ein:get-response-redirect (response)

View file

@ -3,12 +3,13 @@
(defun eintest:notebooklist-make-empty (&optional url-or-port)
"Make empty notebook list buffer."
(let ((url-or-port (or url-or-port ein:testing-notebook-dummy-url)))
(cl-letf (((symbol-function 'ein:need-kernelspecs) #'ignore)
((symbol-function 'ein:content-query-sessions) #'ignore))
(ein:notebooklist-open--finish (or url-or-port ein:testing-notebook-dummy-url) nil nil
(make-ein:$content :url-or-port (or url-or-port ein:testing-notebook-dummy-url)
(ein:notebooklist-open--finish url-or-port #'ignore
(make-ein:$content :url-or-port url-or-port
:notebook-version "5.7.0"
:path ""))))
:path "")))))
(defmacro eintest:notebooklist-is-empty-context-of (func)
`(ert-deftest ,(intern (format "%s--notebooklist" func)) ()