diff --git a/ein-kernel.el b/ein-kernel.el index 4eae032..94b85fa 100644 --- a/ein-kernel.el +++ b/ein-kernel.el @@ -31,7 +31,7 @@ (require 'ein-utils) (require 'ein-websocket) (require 'ein-events) -(require 'url) +(require 'ein-query) (defstruct ein:$kernel @@ -90,17 +90,14 @@ FIXME: document other slots." (defun ein:kernel-start (kernel notebook-id) "Start kernel of the notebook whose id is NOTEBOOK-ID." (unless (ein:$kernel-running kernel) - (let* ((qs (format "notebook=%s" notebook-id)) - (url (concat (ein:url (ein:$kernel-url-or-port kernel) - (ein:$kernel-base-url kernel)) - "?" qs)) - (url-request-method "POST")) - (url-retrieve - url - (lambda (status kernel) - (ein:kernel--kernel-started kernel (ein:json-read)) - (kill-buffer (current-buffer))) - (list kernel))))) + (ein:query-ajax + (concat (ein:url (ein:$kernel-url-or-port kernel) + (ein:$kernel-base-url kernel)) + "?" (format "notebook=%s" notebook-id)) + :type "POST" + :parser #'ein:json-read + :success (cons #'ein:kernel--kernel-started kernel) + :timeout 5000))) (defun ein:kernel-restart (kernel) @@ -109,23 +106,21 @@ FIXME: document other slots." (ein:log 'info "Restarting kernel") (when (ein:$kernel-running kernel) (ein:kernel-stop-channels kernel) - (let ((url (ein:url (ein:$kernel-url-or-port kernel) - (ein:$kernel-kernel-url kernel) - "restart")) - (url-request-method "POST")) - (url-retrieve - url - (lambda (status kernel) - (ein:kernel--kernel-started kernel (ein:json-read)) - (kill-buffer (current-buffer))) - (list kernel))))) + (ein:query-ajax + (ein:url (ein:$kernel-url-or-port kernel) + (ein:$kernel-kernel-url kernel) + "restart") + :type "POST" + :parser #'ein:json-read + :success (cons #'ein:kernel--kernel-started kernel) + :timeout 5000))) -(defun ein:kernel--kernel-started (kernel json) - (ein:log 'info "Kernel started: %s" (plist-get json :kernel_id)) +(defun* ein:kernel--kernel-started (kernel &key data &allow-other-keys) + (ein:log 'info "Kernel started: %s" (plist-get data :kernel_id)) (setf (ein:$kernel-running kernel) t) - (setf (ein:$kernel-kernel-id kernel) (plist-get json :kernel_id)) - (setf (ein:$kernel-ws-url kernel) (plist-get json :ws_url)) + (setf (ein:$kernel-kernel-id kernel) (plist-get data :kernel_id)) + (setf (ein:$kernel-ws-url kernel) (plist-get data :ws_url)) (setf (ein:$kernel-kernel-url kernel) (concat (ein:$kernel-base-url kernel) "/" (ein:$kernel-kernel-id kernel))) @@ -362,29 +357,29 @@ http://ipython.org/ipython-doc/dev/development/messaging.html#complete (defun ein:kernel-interrupt (kernel) (when (ein:$kernel-running kernel) (ein:log 'info "Interrupting kernel") - (let ((url (ein:url (ein:$kernel-url-or-port kernel) - (ein:$kernel-kernel-url kernel) - "interrupt")) - (url-request-method "POST")) - (url-retrieve - url - (lambda (s) - (ein:log 'info "Sent interruption command.") - (kill-buffer (current-buffer))))))) + (ein:query-ajax + (ein:url (ein:$kernel-url-or-port kernel) + (ein:$kernel-kernel-url kernel) + "interrupt") + :type "POST" + :success (cons (lambda (&rest ignore) + (ein:log 'info "Sent interruption command.")) + nil) + :timeout 5000))) (defun ein:kernel-kill (kernel) (when (ein:$kernel-running kernel) (setf (ein:$kernel-running kernel) nil) - (let ((url-request-method "DELETE") - (url (ein:url-no-cache - (ein:url (ein:$kernel-url-or-port kernel) - (ein:$kernel-kernel-url kernel))))) - (url-retrieve - url - (lambda (s) - (ein:log 'info "Notebook kernel is killed") - (kill-buffer (current-buffer))))))) + (ein:query-ajax + (ein:url (ein:$kernel-url-or-port kernel) + (ein:$kernel-kernel-url kernel)) + :cache nil + :type "DELETE" + :success (cons (lambda (&rest ignore) + (ein:log 'info "Notebook kernel is killed")) + nil) + :timeout 5000))) ;; Reply handlers. diff --git a/ein-log.el b/ein-log.el index 9439b92..7bfa063 100644 --- a/ein-log.el +++ b/ein-log.el @@ -92,16 +92,23 @@ (get-buffer-create (format ein:log-buffer-name-template name))) (ein:log 'verbose "Start logging.")) +;; FIXME: this variable must go to somewhere more central +(defvar ein:debug nil + "Set to non-`nil' to raise errors instead of suppressing it. +Change the behavior of `ein:log-ignore-errors'.") + (defmacro ein:log-ignore-errors (&rest body) "Execute BODY; if an error occurs, log the error and return nil. Otherwise, return result of last form in BODY." (declare (debug t) (indent 0)) - `(condition-case err + `(if ein:debug (progn ,@body) - (error - (ein:log 'debug "Error: %S" err) - (ein:log 'error (error-message-string err)) - nil))) + (condition-case err + (progn ,@body) + (error + (ein:log 'debug "Error: %S" err) + (ein:log 'error (error-message-string err)) + nil)))) (defun ein:log-del () "Kill buffer `ein:log-buffer'." diff --git a/ein-notebook.el b/ein-notebook.el index 8eebcc1..dc3f3ea 100644 --- a/ein-notebook.el +++ b/ein-notebook.el @@ -21,7 +21,14 @@ ;;; Commentary: -;; +;; * Coding rule about current buffer. +;; A lot of notebook and cell functions touches to current buffer and +;; it is not ideal to wrap all these functions by `with-current-buffer'. +;; Therefore, when the function takes `notebook' to the first argument +;; ("method" function), it is always assumed that the current buffer +;; is the notebook buffer. **However**, functions called as callback +;; (via `url-retrieve', for example) must protect themselves by +;; calling from unknown buffer. ;;; Code: @@ -39,6 +46,7 @@ (require 'ein-events) (require 'ein-notification) (require 'ein-kill-ring) +(require 'ein-query) ;;; Configuration @@ -62,12 +70,13 @@ yet. So be careful when using EIN functions. They may change." ) :group 'ein) -(defun ein:notebook-discard-output-p () +(defun ein:notebook-discard-output-p (notebook) "Return non-`nil' if the output must be discarded, otherwise save." (case ein:notebook-discard-output-on-save (no nil) (yes t) - (t (funcall ein:notebook-discard-output-on-save)))) + (t (with-current-buffer (ein:notebook-buffer notebook) + (funcall ein:notebook-discard-output-on-save))))) ;;; Class and variable @@ -212,18 +221,18 @@ the time of execution." (let ((url (ein:notebook-url-from-url-and-id url-or-port notebook-id)) (notebook (ein:notebook-new url-or-port notebook-id))) (ein:log 'debug "Opening notebook at %s" url) - (url-retrieve url - #'ein:notebook-url-retrieve-callback - (list notebook)) + (ein:query-ajax + url + :parser #'ein:json-read + :success (cons #'ein:notebook-request-open-callback notebook)) notebook)) -(defun ein:notebook-url-retrieve-callback (status notebook) +(defun* ein:notebook-request-open-callback (notebook &key status data + &allow-other-keys) (ein:log 'debug "URL-RETRIEVE nodtebook-id = %S, status = %S" (ein:$notebook-notebook-id notebook) status) - (let ((data (ein:json-read)) - (notebook-id (ein:$notebook-notebook-id notebook))) - (kill-buffer (current-buffer)) + (let ((notebook-id (ein:$notebook-notebook-id notebook))) (ein:notebook-init notebook data) (with-current-buffer (ein:notebook-get-buffer notebook) (ein:log-setup (ein:$notebook-notebook-id notebook)) @@ -738,7 +747,7 @@ Do not clear input prompts when the prefix argument is given." (defun ein:notebook-to-json (notebook) "Return json-ready alist." - (let* ((discard (ein:notebook-discard-output-p)) + (let* ((discard (ein:notebook-discard-output-p notebook)) (cells (mapcar (lambda (c) (ein:cell-to-json c discard)) (ein:notebook-get-cells notebook)))) `((worksheets . [((cells . ,(apply #'vector cells)))]) @@ -751,63 +760,54 @@ Do not clear input prompts when the prefix argument is given." (push `(nbformat . ,(ein:$notebook-nbformat notebook)) data) (ein:events-trigger (ein:$notebook-events notebook) '(notebook_saving . Notebook)) - (let ((url (ein:url-no-cache (ein:notebook-url notebook))) - (url-request-method "PUT") - (url-request-extra-headers '(("Content-Type" . "application/json"))) - (url-request-data (json-encode data))) - (ein:log 'debug "URL-RETRIEVE url = %s" url) - (ein:log 'debug "URL-REQUEST-DATA = %s" url-request-data) - (url-retrieve - url - #'ein:notebook-save-notebook-callback - (list notebook retry))))) + (ein:query-ajax + (ein:notebook-url notebook) + :type "PUT" + :headers '(("Content-Type" . "application/json")) + :cache nil + :data (json-encode data) + :error (cons #'ein:notebook-save-notebook-error notebook) + :success (cons #'ein:notebook-save-notebook-workaround + (cons notebook retry)) + :status-code + `((204 . ,(cons #'ein:notebook-save-notebook-success notebook))) + :timeout 5000))) (defun ein:notebook-save-notebook-command () (interactive) (ein:notebook-save-notebook ein:notebook 0)) -(defun ein:notebook-save-notebook-callback (status notebook retry) - (declare (special url-http-response-status - url-http-method)) - (ein:log 'debug "SAVE-NOTEBOOK-CALLBACK nodtebook-id = %S, status = %S" - (ein:$notebook-notebook-id notebook) - status) - (ein:log 'debug "url-http-response-status = %s" url-http-response-status) - (ein:log 'debug "url-request-method = %s" url-request-method) - (ein:log 'debug "url-http-method = %s" (when (boundp 'url-http-method) - url-http-method)) - (ein:log 'debug "(buffer-string) = \n%s" (buffer-string)) - (let ((response url-http-response-status)) - ;; ^-- "save" local variable before killing buffer. - (kill-buffer (current-buffer)) - (with-current-buffer (ewoc-buffer (ein:$notebook-ewoc notebook)) - (ein:aif (plist-get status :error) - (progn - (ein:log 'debug "ERROR CODE = %S" it) - (ein:notebook-save-notebook-error notebook status)) - ;; IPython server returns 204 only when the notebook URL is - ;; accessed via PUT or DELETE. As it seems Emacs failed to - ;; choose PUT method every two times, let's check the response - ;; here and fail when 204 is not returned. - (if (eq response 204) - (ein:notebook-save-notebook-success notebook status) - (if (< retry ein:notebook-save-retry-max) - (progn - (ein:log 'info "Retry saving... Next count: %s" (1+ retry)) - (ein:notebook-save-notebook notebook (1+ retry))) - (ein:notebook-save-notebook-error notebook status) - (ein:log 'info - "Status code (=%s) is not 204 and retry exceeds limit (=%s)." - response ein:notebook-save-retry-max))))))) +(defun* ein:notebook-save-notebook-workaround (nb-retry &rest args + &key + status + response-status + &allow-other-keys) + ;; IPython server returns 204 only when the notebook URL is + ;; accessed via PUT or DELETE. As it seems Emacs failed to + ;; choose PUT method every two times, let's check the response + ;; here and fail when 204 is not returned. + (unless (eq response-status 204) + (let ((notebook (car nb-retry)) + (retry (cdr nb-retry))) + (with-current-buffer (ein:notebook-buffer notebook) + (if (< retry ein:notebook-save-retry-max) + (progn + (ein:log 'info "Retry saving... Next count: %s" (1+ retry)) + (ein:notebook-save-notebook notebook (1+ retry))) + (ein:notebook-save-notebook-error notebook :status status) + (ein:log 'info + "Status code (=%s) is not 204 and retry exceeds limit (=%s)." + response-status ein:notebook-save-retry-max)))))) -(defun ein:notebook-save-notebook-success (notebook status) +(defun ein:notebook-save-notebook-success (notebook &rest ignore) (ein:log 'info "Notebook is saved.") (setf (ein:$notebook-dirty notebook) nil) - (set-buffer-modified-p nil) + (with-current-buffer (ein:notebook-buffer notebook) + (set-buffer-modified-p nil)) (ein:events-trigger (ein:$notebook-events notebook) '(notebook_saved . Notebook))) -(defun ein:notebook-save-notebook-error (notebook status) +(defun ein:notebook-save-notebook-error (notebook &rest ignore) (ein:log 'info "Failed to save notebook!") (ein:events-trigger (ein:$notebook-events notebook) '(notebook_save_failed . Notebook))) diff --git a/ein-notebooklist.el b/ein-notebooklist.el index a5a7ca4..e201d73 100644 --- a/ein-notebooklist.el +++ b/ein-notebooklist.el @@ -71,29 +71,36 @@ (when (and (stringp url-or-port) (string-match "^[0-9]+$" url-or-port)) (setq url-or-port (string-to-number url-or-port))) - (url-retrieve - (ein:notebooklist-url url-or-port) - (if no-popup - #'ein:notebooklist-url-retrieve-callback - (lambda (&rest args) - (pop-to-buffer (apply #'ein:notebooklist-url-retrieve-callback args)))) - (list url-or-port)) + (let ((success + (if no-popup + #'ein:notebooklist-url-retrieve-callback + (lambda (&rest args) + (pop-to-buffer + (apply #'ein:notebooklist-url-retrieve-callback args)))))) + (ein:query-ajax + (ein:notebooklist-url url-or-port) + :cache nil + :parser #'ein:json-read + :success (cons success url-or-port) + :timeout 5000)) (ein:notebooklist-get-buffer url-or-port)) -(defun ein:notebooklist-url-retrieve-callback (status url-or-port) +(defun* ein:notebooklist-url-retrieve-callback (url-or-port + &key + status + data + &allow-other-keys) "Called via `ein:notebooklist-open'." (ein:aif (plist-get status :error) (error "Failed to connect to server '%s'. Got: %S" (ein:url url-or-port) it)) - (let ((data (ein:json-read))) - (kill-buffer (current-buffer)) - (with-current-buffer (ein:notebooklist-get-buffer url-or-port) - (setq ein:notebooklist - (make-ein:$notebooklist :url-or-port url-or-port - :data data)) - (ein:notebooklist-render) - (goto-char (point-min)) - (current-buffer)))) + (with-current-buffer (ein:notebooklist-get-buffer url-or-port) + (setq ein:notebooklist + (make-ein:$notebooklist :url-or-port url-or-port + :data data)) + (ein:notebooklist-render) + (goto-char (point-min)) + (current-buffer))) (defun ein:notebooklist-reload () "Reload current Notebook list." @@ -117,20 +124,24 @@ "Ask server to create a new notebook and update the notebook list buffer." (message "Creating a new notebook...") (unless (setq url-or-port (ein:$notebooklist-url-or-port ein:notebooklist))) - (url-retrieve + (ein:query-ajax (ein:notebooklist-new-url url-or-port) - (lambda (s buffer) - (let ((notebook-id - (ein:notebooklist-get-data-in-body-tag "data-notebook-id"))) - (kill-buffer (current-buffer)) - (message "Creating a new notebook... Done.") - (with-current-buffer buffer - (if notebook-id - (ein:notebooklist-open-notebook ein:notebooklist notebook-id) - (message (concat "Oops. EIN failed to open new notebook. " - "Please find it in the notebook list.")) - (ein:notebooklist-reload))))) - (list (current-buffer)))) + :parser (lambda () + (ein:notebooklist-get-data-in-body-tag "data-notebook-id")) + :success (cons #'ein:notebooklist-new-notebook-callback (current-buffer)) + :timeout 5000)) + +(defun* ein:notebooklist-new-notebook-callback (buffer &key + data + &allow-other-keys) + (let ((notebook-id data)) + (message "Creating a new notebook... Done.") + (with-current-buffer buffer + (if notebook-id + (ein:notebooklist-open-notebook ein:notebooklist notebook-id) + (message (concat "Oops. EIN failed to open new notebook. " + "Please find it in the notebook list.")) + (ein:notebooklist-reload))))) (defun ein:notebooklist-delete-notebook-ask (notebook-id name) (when (y-or-n-p (format "Delete notebook %s?" name)) @@ -138,19 +149,17 @@ (defun ein:notebooklist-delete-notebook (notebook-id name) (message "Deleting notebook %s..." name) - (let ((url (ein:url-no-cache - (ein:notebook-url-from-url-and-id - (ein:$notebooklist-url-or-port ein:notebooklist) - notebook-id))) - (url-request-method "DELETE")) - (url-retrieve - url - (lambda (s buffer name) - (kill-buffer (current-buffer)) - (message "Deleting notebook %s... Done." name) - (with-current-buffer buffer - (ein:notebooklist-reload))) - (list (current-buffer) name)))) + (ein:query-ajax + (ein:notebook-url-from-url-and-id + (ein:$notebooklist-url-or-port ein:notebooklist) + notebook-id) + :cache nil + :type "DELETE" + :success (cons (lambda (packed &rest ignore) + (message "Deleting notebook %s... Done." (cdr packed)) + (with-current-buffer (car packed) + (ein:notebooklist-reload))) + (cons (current-buffer) name)))) (defun ein:notebooklist-render () "Render notebook list widget. diff --git a/ein-query.el b/ein-query.el new file mode 100644 index 0000000..52a38dc --- /dev/null +++ b/ein-query.el @@ -0,0 +1,179 @@ +;;; ein-query.el --- jQuery like interface on to of url-retrieve + +;; Copyright (C) 2012- Takafumi Arakaki + +;; Author: Takafumi Arakaki + +;; This file is NOT part of GNU Emacs. + +;; ein-query.el is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; ein-query.el is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with ein-query.el. If not, see . + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile (require 'cl)) +(require 'url) + +(require 'ein-utils) +(require 'ein-log) + +(defun ein:safe-funcall-packed (packed &rest args) + (when packed + (ein:log-ignore-errors (apply #'ein:funcall-packed packed args)))) + +(defmacro ein:with-live-buffer (buffer &rest body) + "Execute BODY if BUFFER is alive." + (declare (indent 1) (debug t)) + `(when (buffer-live-p ,buffer) (with-current-buffer ,buffer ,@body))) + +(ein:deflocal ein:query-ajax-timer nil) + +(defun* ein:query-ajax (url &rest settings + &key + (cache t) + (type "GET") + (data nil) + (parser nil) + (headers nil) + (success nil) + (error nil) + (timeout nil) + (status-code nil)) + "Mimic `$.ajax'. + +:CACHE (nil/t) : append time-stamp to URL so the URL is always loaded. +:TYPE (string) : sets `url-request-method' +:DATA (string) : sets `url-request-data' +:PARSER (symbol) : a function that reads current buffer and return data +:HEADERS (alist) : sets `url-request-extra-headers' +:SUCCESS (cons) : called on success +:ERROR (cons) : called on error +:TIMEOUT (number) : timeout in millisecond +:STATUS-CODE (alist) : map status code (int) to callback (cons) + +* Callback functions + +All callbacks must be given as `cons' where car is a FUNCTION and +cdr is its first ARGUMENT. It is analogous of `$.proxy'. Call +signature is like this: + \(FUNCTION ARGUMENT [other callback specific arguments]) + +Also note that the callback FUNCTION must be defined +using `defun*' with `&key' and `&allow-other-keys' to ignore +missing/extra arguments as some callback (namely :ERROR) changes +arguments to be passed, depending on situation. + +* :ERROR callback + +:SYMBOL-STATUS (`error'/`timeout') : analogous of `textStatus' +:STATUS (list) : see `url-retrieve' +:RESPONSE-STATUS : = `url-http-response-status' + +* :SUCCESS callback + +This callback takes :DATA (object), which is a data object parsed +by :PARSER. If :PARSER is not specified, this is nil. +The :SUCCESS callback also takes the :STATUS and :RESPONSE-STATUS +argument. + +* :STATUS-CODE callback + +Each value of this alist is a callback which is similar to :ERROR +or :SUCCESS callback. However, current buffer of this callback +is not guaranteed to be the process buffer. + +* :PARSER function + +This is analogous to the `dataType' argument of `$.ajax'. +Only this function can accuses to the process buffer, which +is killed immediately after the execution of this function. + +* See also: http://api.jquery.com/jQuery.ajax/" + (ein:log 'debug "EIN:QUERY-AJAX") + (unless cache + (setq url (ein:url-no-cache url))) + (let* ((url-request-extra-headers headers) + (url-request-method type) + (url-request-data data) + (buffer (url-retrieve url #'ein:query-ajax-callback settings))) + (when timeout + (ein:log 'debug "Start timer: timeout=%s ms" timeout) + (with-current-buffer buffer + (setq ein:query-ajax-timer + (apply #'run-at-time + (/ timeout 1000.0) nil + #'ein:query-ajax-timeout-callback + (cons buffer settings))))) + buffer)) + +(defun* ein:query-ajax-callback (status &key + (headers nil) + (parser nil) + (success nil) + (error nil) + (timeout nil) + (status-code nil) + &allow-other-keys) + (declare (special url-http-response-status)) + + (ein:log 'debug "EIN:QUERY-AJAX-CALLBACK") + (ein:log 'debug "status = %S" status) + (ein:log 'debug "url-http-response-status = %s" url-http-response-status) + (ein:log 'debug "(buffer-string) =\n%s" (buffer-string)) + + (ein:query-ajax-cancel-timer) + (let* ((buffer (current-buffer)) ; `parser' could change buffer... + (response-status url-http-response-status) + (status-code-callback (cdr (assq response-status status-code))) + (status-error (plist-get status :error)) + (data (if (and parser (not status-error)) + (unwind-protect + (funcall parser) + (kill-buffer buffer))))) + (ein:log 'debug "data = %s" data) + + (ein:log 'debug "Executing success/error callback.") + (apply #'ein:safe-funcall-packed + (append (if (plist-get status :error) + (list error :symbol-status 'error) + (list success)) + (list :status status :data data + :response-status response-status))) + + (ein:log 'debug "Executing status-code callback.") + (ein:safe-funcall-packed status-code-callback + :status status :data data))) + +(defun* ein:query-ajax-timeout-callback (buffer &key + (error nil) + &allow-other-keys) + (ein:log 'debug "EIN:QUERY-AJAX-TIMEOUT-CALLBACK buffer = %s" buffer) + (ein:with-live-buffer buffer + (ein:safe-funcall-packed error :symbol-status 'timeout) + (let ((proc (process-buffer buffer))) + (kill-process proc) + (kill-buffer buffer)))) + +(defun ein:query-ajax-cancel-timer () + (ein:log 'debug "EIN:QUERY-AJAX-CANCEL-TIMER") + (when ein:query-ajax-timer + (cancel-timer ein:query-ajax-timer) + (setq ein:query-ajax-timer nil))) + +(provide 'ein-query) + +;;; ein-query.el ends here diff --git a/tests/func-test.el b/tests/func-test.el index 2ba1209..ff197c9 100644 --- a/tests/func-test.el +++ b/tests/func-test.el @@ -2,6 +2,7 @@ (require 'ert) (require 'ein-notebooklist) +(require 'wid-edit) ;; Execute `eintest:dz-ipython-start' before starting the following ;; test to setup server. @@ -39,14 +40,27 @@ Make MAX-COUNT larger \(default 50) to wait longer before timeout." (let ((notebook (eintest:get-notebook-by-name url-or-port "Untitled0"))) (if notebook notebook - (with-current-buffer (ein:notebooklist-get-buffer url-or-port) + (with-current-buffer (ein:notebooklist-open url-or-port t) (setq ein:notebooklist nil) (eintest:wait-until (lambda () ein:notebooklist)) - (setq ein:notebooklist nil) (ein:notebooklist-new-notebook url-or-port) - (eintest:wait-until (lambda () ein:notebooklist))) + (eintest:wait-until + (lambda () (eintest:get-notebook-by-name url-or-port "Untitled0")))) (eintest:get-notebook-by-name url-or-port "Untitled0")))) +(defun eintest:delete-notebook-by-name (url-or-port notebook-name) + (with-current-buffer (ein:notebooklist-open url-or-port nil) + (eintest:wait-until (lambda () ein:notebooklist)) + (save-excursion + (goto-char (point-min)) + (search-forward notebook-name) + (move-beginning-of-line 1) + (search-forward "Delete") + (flet ((y-or-n-p (ignore) t)) + (widget-button-press (point)))) + (setq ein:notebooklist nil) + (eintest:wait-until (lambda () ein:notebooklist)))) + (ert-deftest eintest:get-untitled0-or-create () (let ((notebook (eintest:get-untitled0-or-create eintest:port))) (eintest:wait-until (lambda () (ein:aand (ein:$notebook-kernel notebook) @@ -54,6 +68,16 @@ Make MAX-COUNT larger \(default 50) to wait longer before timeout." (with-current-buffer (ein:notebook-buffer notebook) (should (equal (ein:$notebook-notebook-name ein:notebook) "Untitled0"))))) +(ert-deftest eintest:delete-untitled0 () + (loop + repeat 2 + do (let ((notebook (eintest:get-untitled0-or-create eintest:port))) + (eintest:wait-until + (lambda () (ein:aand (ein:$notebook-kernel notebook) + (ein:kernel-ready-p it))))) + do (eintest:delete-notebook-by-name eintest:port "Untitled0") + do (should-not (eintest:get-notebook-by-name eintest:port "Untitled0")))) + (ert-deftest ein:notebook-execute-current-cell-simple () (let ((notebook (eintest:get-untitled0-or-create eintest:port))) (eintest:wait-until (lambda () (ein:aand (ein:$notebook-kernel notebook) diff --git a/tests/test-ein-kernel.el b/tests/test-ein-kernel.el index e4dfe23..ef2c770 100644 --- a/tests/test-ein-kernel.el +++ b/tests/test-ein-kernel.el @@ -12,8 +12,9 @@ (let* ((kernel (eintest:kernel-new 8888)) (notebook-id "NOTEBOOK-ID") (desired-url "http://127.0.0.1:8888/kernels?notebook=NOTEBOOK-ID") + (dummy-buffer (get-buffer-create "*eintest:dummy*")) got-url) - (flet ((url-retrieve (url &rest ignore) (setq got-url url))) + (flet ((url-retrieve (url &rest ignore) (setq got-url url) dummy-buffer)) (ein:kernel-start kernel notebook-id) (should (equal got-url desired-url))))) @@ -21,12 +22,13 @@ (let* ((kernel (eintest:kernel-new 8888)) (kernel-id "KERNEL-ID") (desired-url "http://127.0.0.1:8888/kernels/KERNEL-ID/restart") + (dummy-buffer (get-buffer-create "*eintest:dummy*")) got-url) - (flet ((url-retrieve (url &rest ignore) (setq got-url url)); + (flet ((url-retrieve (url &rest ignore) (setq got-url url) dummy-buffer) (ein:kernel-stop-channels (&rest ignore)) (ein:websocket (&rest ignore) (make-ein:$websocket)) (ein:events-trigger (&rest ignore))) - (ein:kernel--kernel-started kernel (list :kernel_id kernel-id)) + (ein:kernel--kernel-started kernel :data (list :kernel_id kernel-id)) (ein:kernel-restart kernel) (should (equal got-url desired-url))))) @@ -35,11 +37,12 @@ (let* ((kernel (eintest:kernel-new 8888)) (kernel-id "KERNEL-ID") (desired-url "http://127.0.0.1:8888/kernels/KERNEL-ID/interrupt") + (dummy-buffer (get-buffer-create "*eintest:dummy*")) got-url) - (flet ((url-retrieve (url &rest ignore) (setq got-url url)) + (flet ((url-retrieve (url &rest ignore) (setq got-url url) dummy-buffer) (ein:kernel-stop-channels (&rest ignore)) (ein:websocket (&rest ignore) (make-ein:$websocket))) - (ein:kernel--kernel-started kernel (list :kernel_id kernel-id)) + (ein:kernel--kernel-started kernel :data (list :kernel_id kernel-id)) (ein:kernel-interrupt kernel) (should (equal got-url desired-url))))) @@ -47,11 +50,12 @@ (let* ((kernel (eintest:kernel-new 8888)) (kernel-id "KERNEL-ID") (desired-url "http://127.0.0.1:8888/kernels/KERNEL-ID") + (dummy-buffer (get-buffer-create "*eintest:dummy*")) got-url) - (flet ((url-retrieve (url &rest ignore) (setq got-url url)) + (flet ((url-retrieve (url &rest ignore) (setq got-url url) dummy-buffer) (ein:kernel-stop-channels (&rest ignore)) (ein:websocket (&rest ignore) (make-ein:$websocket))) - (ein:kernel--kernel-started kernel (list :kernel_id kernel-id)) + (ein:kernel--kernel-started kernel :data (list :kernel_id kernel-id)) (ein:kernel-kill kernel) (let* ((l (split-string got-url "?")) (got-url-0 (nth 0 l)) diff --git a/tests/test-ein-notebook.el b/tests/test-ein-notebook.el index 2db4740..a9d314d 100644 --- a/tests/test-ein-notebook.el +++ b/tests/test-ein-notebook.el @@ -44,19 +44,16 @@ (defun eintest:notebook-from-json (json-string &optional notebook-id) (unless notebook-id (setq notebook-id "NOTEBOOK-ID")) - (with-temp-buffer - (erase-buffer) - (insert json-string) - (flet ((pop-to-buffer (buf) buf) - (ein:notebook-start-kernel ())) - (with-current-buffer (ein:notebook-url-retrieve-callback - nil - (ein:notebook-new "DUMMY-URL" notebook-id)) - (let ((events (ein:events-new (current-buffer)))) - (setf (ein:$notebook-events ein:notebook) events) - (setf (ein:$notebook-kernel ein:notebook) - (ein:kernel-new 8888 "/kernels" events))) - (current-buffer))))) + (flet ((pop-to-buffer (buf) buf) + (ein:notebook-start-kernel ())) + (with-current-buffer (ein:notebook-request-open-callback + (ein:notebook-new "DUMMY-URL" notebook-id) + :data (ein:json-read-from-string json-string)) + (let ((events (ein:events-new (current-buffer)))) + (setf (ein:$notebook-events ein:notebook) events) + (setf (ein:$notebook-kernel ein:notebook) + (ein:kernel-new 8888 "/kernels" events))) + (current-buffer)))) (defun eintest:notebook-make-data (cells &optional name) (unless name (setq name "Dummy Name"))